Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>Vars.add(scope.getVar(root.getString())); } } /** * Whether the given variable is forbidden from being inlined. */ private boolean isVarInlineForbidden(Var var) { // A variable may not be inlined if: // 1) The variable is exported, // 2) A reference to the variable has been inlined. We're downstream // of the mechanism that creates variable references, so we don't // have a good way to update the reference. Just punt on it. // 3) Don't inline the special RENAME_PROPERTY_FUNCTION_NAME return compiler.getCodingConvention().isExported(var.name) || RenameProperties.RENAME_PROPERTY_FUNCTION_NAME.equals(var.name) || staleVars.contains(var); } /** * Do the actual work of inlining a single declaration into a single * reference. */ private void inline(Var v, Reference declaration, Reference init, Reference reference) { Node value = init.getAssignedValue(); Preconditions.checkState(value != null); // Check for function declarations before the value is moved in the AST. boolean isFunctionDeclaration = NodeUtil.isFunctionDeclaration(value); inlineValue(v, reference, value.detachFromParent()); if (declaration != init) { Node expressRoot = init.getGrandparent(); Preconditions.checkState(expressRoot.getType() == Token.EXPR_RESULT); NodeUtil.removeChild(expressRoot.getParent(), expressRoot); } // Function declarations have already been removed. if (!isFunctionDeclaration) { removeDeclaration(declaration); } else { compiler.reportCodeChange(); } } /** * Inline an immutable variable into all of its references. */ private void inlineWellDefinedVariable(Var v, Node value, List<Reference> refSet) { Reference decl = refSet.get(0); for (int i = 1; i < refSet.size(); i++) { inlineValue(v, refSet.get(i), value.cloneTree()); } removeDeclaration(decl); } /** * Inline a declared constant. */ private void inlineDeclaredConstant(Var v, Node value, List<Reference> refSet) { //

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> return false; } // Is the constant's value immutable? if (!NodeUtil.isImmutableValue(value)) { return false; } // Determine if we should really inline a String or not. return value.getType() != Token.STRING || isStringWorthInlining(var, refInfo.references); } /** * Compute whether the given string is worth inlining. */ private boolean isStringWorthInlining(Var var, List<Reference> refs) { if (!inlineAllStrings && !var.isDefine()) { int len = var.getInitialValue().getString().length() + "''".length(); // if not inlined: var xx="value"; .. xx .. xx .. // The 4 bytes per reference is just a heuristic: // 2 bytes per var name plus maybe 2 bytes if we don't inline, e.g. // in the case of "foo " + CONST + " bar" int noInlineBytes = "var xx=;".length() + len + 4 * (refs.size() - 1); // if inlined: // I'm going to assume that half of the quotes will be eliminated // thanks to constant folding, therefore I subtract 1 (2/2=1) from // the string length. int inlineBytes = (len - 1) * (refs.size() - 1); // Not inlining if doing so uses more bytes, or this constant is being // defined. return noInlineBytes >= inlineBytes; } return true; } /** * @return true if the provided reference and declaration can be safely * inlined according to our criteria */ private boolean canInline( Reference declaration, Reference initialization, Reference reference) { if (!isValidDeclaration(declaration) || !isValidInitialization(initialization) || !isValidReference(reference)) { return false; } // If the value is read more than once, skip it. // VAR declarations and EXPR_RESULT don't need the value, but other // ASSIGN expressions parents do. if (declaration != initialization && initialization.getGrandparent().getType() != Token.EXPR_RESULT) { return false; } // Be very conservative and do no cross control structures or

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> case Token.SHEQ: case Token.SHNE: case Token.CASE: Node left; Node right; if (operatorToken == Token.CASE) { left = condition.getParent().getFirstChild(); // the switch condition right = condition.getFirstChild(); } else { left = condition.getFirstChild(); right = condition.getLastChild(); } Node typeOfNode = null; Node stringNode = null; if (left.getType() == Token.TYPEOF && right.getType() == Token.STRING) { typeOfNode = left; stringNode = right; } else if (right.getType() == Token.TYPEOF && left.getType() == Token.STRING) { typeOfNode = right; stringNode = left; } if (typeOfNode != null && stringNode != null) { Node operandNode = typeOfNode.getFirstChild(); JSType operandType = getTypeIfRefinable(operandNode, blindScope); if (operandType != null) { boolean resultEqualsValue = operatorToken == Token.EQ || operatorToken == Token.SHEQ || operatorToken == Token.CASE; if (!outcome) { resultEqualsValue = !resultEqualsValue; } return caseTypeOf(operandNode, operandType, stringNode.getString(), resultEqualsValue, blindScope); } } } switch (operatorToken) { case Token.AND: if (outcome) { return caseAndOrNotShortCircuiting(condition.getFirstChild(), condition.getLastChild(), blindScope, true); } else { return caseAndOrMaybeShortCircuiting(condition.getFirstChild(), condition.getLastChild(), blindScope, true); } case Token.OR: if (!outcome) { return caseAndOrNotShortCircuiting(condition.getFirstChild(), condition.getLastChild(), blindScope, false); } else { return caseAndOrMaybeShortCircuiting(condition.getFirstChild(), condition.getLastChild(), blindScope, false); } case Token.EQ: if (outcome) { return caseEquality(condition, blindScope, EQ); } else { return caseEquality(condition, blindScope, NE); } case Token.NE: if (outcome) { return caseEquality(condition

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>, blindScope, NE); } else { return caseEquality(condition, blindScope, EQ); } case Token.SHEQ: if (outcome) { return caseEquality(condition, blindScope, SHEQ); } else { return caseEquality(condition, blindScope, SHNE); } case Token.SHNE: if (outcome) { return caseEquality(condition, blindScope, SHNE); } else { return caseEquality(condition, blindScope, SHEQ); } case Token.NAME: case Token.GETPROP: return caseNameOrGetProp(condition, blindScope, outcome); case Token.ASSIGN: return firstPreciserScopeKnowingConditionOutcome( condition.getFirstChild(), firstPreciserScopeKnowingConditionOutcome( condition.getFirstChild().getNext(), blindScope, outcome), outcome); case Token.NOT: return firstPreciserScopeKnowingConditionOutcome( condition.getFirstChild(), blindScope, !outcome); case Token.LE: case Token.LT: case Token.GE: case Token.GT: if (outcome) { return caseEquality(condition, blindScope, INEQ); } break; case Token.INSTANCEOF: return caseInstanceOf( condition.getFirstChild(), condition.getLastChild(), blindScope, outcome); case Token.IN: if (outcome && condition.getFirstChild().getType() == Token.STRING) { return caseIn(condition.getLastChild(), condition.getFirstChild().getString(), blindScope); } break; case Token.CASE: Node left = condition.getParent().getFirstChild(); // the switch condition Node right = condition.getFirstChild(); if (outcome) { return caseEquality(left, right, blindScope, SHEQ); } else { return caseEquality(left, right, blindScope, SHNE); } } return nextPreciserScopeKnowingConditionOutcome( condition, blindScope, outcome); } private FlowScope caseEquality(Node condition, FlowScope blindScope, Function<TypePair, TypePair> merging) { return caseEquality(condition.getFirstChild(), condition.getLastChild(), blindScope, merging); } private FlowScope caseEquality(Node

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> 1; String name = node.getFirstChild().getString(); // Store the context for this label name. LabelInfo li = new LabelInfo(currentDepth); Preconditions.checkState(!current.renameMap.containsKey(name)); current.renameMap.put(name, li); // Create a new name, if needed, for this depth. if (names.size() < currentDepth) { names.add(nameGenerator.generateNextName()); } String newName = getNameForId(currentDepth); compiler.addToDebugLog("label renamed: " + name + " => " + newName); } return true; } /** * Delegate the actual processing of the node to visitLabel and * visitBreakOrContinue. * * {@inheritDoc} */ public void visit(NodeTraversal nodeTraversal, Node node, Node parent) { switch (node.getType()) { case Token.LABEL: visitLabel(node, parent); break; case Token.BREAK: case Token.CONTINUE: visitBreakOrContinue(node); break; } } /** * Rename label references in breaks and continues. * @param node The break or continue node. */ private void visitBreakOrContinue(Node node) { Node nameNode = node.getFirstChild(); if (nameNode != null) { // This is a named break or continue; String name = nameNode.getString(); Preconditions.checkState(name.length() != 0); LabelInfo li = getLabelInfo(name); if (li != null) { String newName = getNameForId(li.id); // Mark the label as referenced so it isn't removed. li.referenced = true; if (!name.equals(newName)) { // Give it the short name. nameNode.setString(newName); compiler.reportCodeChange(); } } } } /** * Rename or remove labels. * @param node The label node. * @param parent The parent of the label node. */ private void visitLabel(Node node, Node parent) { Node nameNode = node.getFirstChild(); Preconditions.checkState(nameNode != null); String name = nameNode.getString(); LabelInfo li = getLabelInfo

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> == Token.COMMA) { Node gramps = parent.getParent(); if (gramps.getType() == Token.CALL && parent == gramps.getFirstChild()) { // Semantically, a direct call to eval is different from an indirect // call to an eval. See Ecma-262 S15.1.2.1. So it's ok for the first // expression to a comma to be a no-op if it's used to indirect // an eval. if (n == parent.getFirstChild() && parent.getChildCount() == 2 && n.getNext().getType() == Token.NAME && "eval".equals(n.getNext().getString())) { return; } } if (n == parent.getLastChild()) { for (Node an : parent.getAncestors()) { int ancestorType = an.getType(); if (ancestorType == Token.COMMA) continue; if (ancestorType != Token.EXPR_RESULT && ancestorType != Token.BLOCK) return; else break; } } } else if (pt != Token.EXPR_RESULT && pt != Token.BLOCK) { if (pt == Token.FOR && parent.getChildCount() == 4 && (n == parent.getFirstChild() || n == parent.getFirstChild().getNext().getNext())) { // Fall through and look for warnings for the 1st and 3rd child // of a for. } else { return; // it might be ok to not have a side-effect } } if (NodeUtil.isSimpleOperatorType(n.getType()) || !NodeUtil.mayHaveSideEffects(n, t.getCompiler())) { if (n.isQualifiedName() && n.getJSDocInfo() != null) { // This no-op statement was there so that JSDoc information could // be attached to the name. This check should not complain about it. return; } else if (NodeUtil.isExpressionNode(n)) { // we already reported the problem when we visited the child. return; } String msg = "This code lacks side-effects. Is there a bug?"; if (n.getType() == Token.STRING) { msg =

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> return false; } // In compiled code, '$' is often a namespace delimiter. To allow inlining // of namespaced constants, we strip off any namespaces here. int pos = name.lastIndexOf('$'); if (pos >= 0) { name = name.substring(pos + 1); if (name.length() == 0) { return false; } } return isConstantKey(name); } @Override public boolean isConstantKey(String name) { if (name.isEmpty() || !Character.isUpperCase(name.charAt(0))) { return false; } // hack way of checking that there aren't any lower-case letters return name.toUpperCase().equals(name); } /** * {@inheritDoc} * * <p>This enforces Google's convention about enum key names. They must match * the regular expression {@code [A-Z0-9][A-Z0-9_]*}. * * <p>Examples: * <ul> * <li>A</li> * <li>213</li> * <li>FOO_BAR</li> * </ul> */ @Override public boolean isValidEnumKey(String key) { return ENUM_KEY_PATTERN.matcher(key).matches(); } /** * {@inheritDoc} * * <p>In Google code, parameter names beginning with {@code opt_} are * treated as optional arguments. */ @Override public boolean isOptionalParameter(Node parameter) { return parameter.getString().startsWith(OPTIONAL_ARG_PREFIX); } @Override public boolean isVarArgsParameter(Node parameter) { return VAR_ARGS_NAME.equals(parameter.getString()); } /** * {@inheritDoc} * * <p>In Google code, any global name starting with an underscore is * considered exported. */ @Override public boolean isExported(String name, boolean local) { return !local && name.startsWith("_"); } /** * {@inheritDoc} * * <p>In Google code, private names end with an underscore, and exported * names are never considered private (see {@link #isExported}). */ @Override public boolean is

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>/* * Copyright 2009 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp.parsing; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import com.google.javascript.jscomp.mozilla.rhino.CompilerEnvirons; import com.google.javascript.jscomp.mozilla.rhino.Context; import com.google.javascript.jscomp.mozilla.rhino.ErrorReporter; import com.google.javascript.jscomp.mozilla.rhino.EvaluatorException; import com.google.javascript.jscomp.mozilla.rhino.Parser; import com.google.javascript.jscomp.mozilla.rhino.ast.AstRoot; import com.google.javascript.rhino.Node; import java.io.IOException; import java.util.ResourceBundle; import java.util.Set; import java.util.logging.Logger; public class ParserRunner { private static final String configResource = "com.google.javascript.jscomp.parsing.ParserConfig"; private static Set<String> annotationNames = null; private static Set<String> suppressionNames = null; // Should never need to instantiate class of static methods. private ParserRunner() {} public static Config createConfig(boolean isIdeMode) { initResourceConfig(); return new Config(annotationNames, suppressionNames, isIdeMode); } private static synchronized void initResourceConfig() { if (annotationNames != null) { return; } ResourceBundle config = ResourceBundle.getBundle(configResource); annotationNames = extractList(config.getString("jsdoc.annotations

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>")); suppressionNames = extractList(config.getString("jsdoc.suppressions")); } private static Set<String> extractList(String configProp) { String[] names = configProp.split(","); Set<String> trimmedNames = Sets.newHashSet(); for (String name : names) { trimmedNames.add(name.trim()); } return ImmutableSet.copyOf(trimmedNames); } /** * Parses the JavaScript text given by a reader. * * @param sourceName The filename. * @param sourceString Source code from the file. * @param errorReporter An error. * @param logger A logger. * @return The AST of the given text. * @throws IOException */ public static Node parse(String sourceName, String sourceString, Config config, ErrorReporter errorReporter, Logger logger) throws IOException { Context cx = Context.enter(); cx.setErrorReporter(errorReporter); cx.setLanguageVersion(Context.VERSION_1_5); CompilerEnvirons compilerEnv = new CompilerEnvirons(); compilerEnv.initFromContext(cx); compilerEnv.setRecordingComments(true); compilerEnv.setRecordingLocalJsDocComments(true); compilerEnv.setWarnTrailingComma(true); if (config.isIdeMode) { compilerEnv.setReservedKeywordAsIdentifier(true); compilerEnv.setAllowMemberExprAsFunctionName(true); } Parser p = new Parser(compilerEnv, errorReporter); AstRoot astRoot = null; try { astRoot = p.parse(sourceString, sourceName, 1); } catch (EvaluatorException e) { logger.info("Error parsing " + sourceName + ": " + e.getMessage()); } finally { Context.exit(); } Node root = null; if (astRoot != null) { root = IRFactory.transformTree( astRoot, sourceString, config, errorReporter); root.setIsSyntheticBlock(true); } return root; } }

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> this.callback = callback; } public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { // Function calls case Token.CALL: Node child = n.getFirstChild(); String name = null; // NOTE: The normalization pass insures that local names do not // collide with global names. if (child.getType() == Token.NAME) { name = child.getString(); } else if (child.getType() == Token.FUNCTION) { name = anonFunctionMap.get(child); } else if (NodeUtil.isFunctionObjectCall(n)) { Preconditions.checkState(NodeUtil.isGet(child)); Node fnIdentifingNode = child.getFirstChild(); if (fnIdentifingNode.getType() == Token.NAME) { name = fnIdentifingNode.getString(); } else if (fnIdentifingNode.getType() == Token.FUNCTION) { name = anonFunctionMap.get(fnIdentifingNode); } } if (name != null) { FunctionState fs = functionMap.get(name); // Only visit call-sites for functions that can be inlined. if (fs != null) { callback.visitCallSite(t, n, parent, fs); } } break; } } } /** * Find references to functions that are inlinable. */ private class FindCandidatesReferences extends CallVisitor implements CallVisitorCallback { FindCandidatesReferences( Map<String, FunctionState> fns, Map<Node, String> anonFns) { super(fns, anonFns, null); this.callback = this; } @Override public void visit(NodeTraversal t, Node n, Node parent) { super.visit(t, n, parent); if (n.getType() == Token.NAME) { checkNameUsage(t, n, parent); } } public void visitCallSite( NodeTraversal t, Node callNode, Node parent, FunctionState fs) { maybeAddReference(t, fs, callNode, t.getModule()); } void maybeAddReference(NodeTraversal t, FunctionState fs, Node callNode, JSModule module)

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>parent.getType() == Token.CALL && parent.getFirstChild() == n) { // This is a normal reference to the function. return; } // Check for a ".call" to the named function: // CALL // GETPROP/GETELEM // NAME // STRING == "call" // This-Value // Function-parameter-1 // ... if (NodeUtil.isGet(parent) && n == parent.getFirstChild() && n.getNext().getType() == Token.STRING && n.getNext().getString().equals("call")) { Node gramps = n.getAncestor(2); if (gramps.getType() == Token.CALL && gramps.getFirstChild() == parent) { // Yep, a ".call". return; } } // Other refs to a function name remove its candidacy for inlining String name = n.getString(); FunctionState fs = fns.get(name); if (fs == null) { return; } // If the name is being assigned to it can not be inlined. if (parent.getType() == Token.ASSIGN && parent.getFirstChild() == n) { // e.g. bar = something; <== we can't inline "bar" // so mark the function as uninlinable. // TODO(johnlenz): Should we just remove it from fns here? fs.setInline(false); } else { // e.g. var fn = bar; <== we can't inline "bar" // As this reference can't be inlined mark the function as // unremovable. fs.setRemove(false); } } } /** * Inline functions at the call sites. */ private class Inline implements CallVisitorCallback { private final FunctionInjector injector; Inline(FunctionInjector injector) { this.injector = injector; } public void visitCallSite( NodeTraversal t, Node callNode, Node parent, FunctionState fs) { Preconditions.checkState(fs.hasExistingFunctionDefinition()); if (fs.canInline()) { Reference ref = fs.getReference(callNode); // There are two cases ref can be null: if the call site

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>ConflictsForFunction(fs); } } /** * @see #resolveInlineConflicts */ private void resolveInlineConflictsForFunction(FunctionState fs) { // Functions that aren't referenced don't cause conflicts. if (!fs.hasReferences()) { return; } Node fnNode = fs.getFn().getFunctionNode(); Set<String> names = findCalledFunctions(fnNode); if (!names.isEmpty()) { // Prevent the removal of the referenced functions. for (String name : names) { FunctionState fsCalled = fns.get(name); if (fsCalled != null && fsCalled.canRemove()) { fsCalled.setRemove(false); // For functions that can no longer be removed, check if they should // still be inlined. if (!mimimizeCost(fsCalled)) { // It can't be inlined remove it from the list. fsCalled.setInline(false); } } } // Make a copy of the Node, so it isn't changed by other inlines. fs.setSafeFnNode(fs.getFn().getFunctionNode().cloneTree()); } } /** * This functions that may be called directly. */ private Set<String> findCalledFunctions(Node node) { Set<String> changed = Sets.newHashSet(); findCalledFunctions(node, changed); return changed; } /** * @see #findCalledFunctions(Node) */ private void findCalledFunctions( Node node, Set<String> changed) { Preconditions.checkArgument(changed != null); // For each referenced function, add a new reference if (node.getType() == Token.CALL) { Node child = node.getFirstChild(); if (child.getType() == Token.NAME) { String name = child.getString(); changed.add(name); } } for (Node c = node.getFirstChild(); c != null; c = c.getNext()) { findCalledFunctions(c, changed); } } /** * For any call-site that needs it, prepare the call-site for inlining * by rewriting the containing expression. */ private void decomposeExpressions(Set<String> fnNames) {

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>newHashMap(); } references.put(ref.callNode, ref); } public Collection<Reference> getReferences() { return getReferencesInternal().values(); } public Reference getReference(Node n) { return getReferencesInternal().get(n); } public Set<String> getNamesToAlias() { if (namesToAlias == null) { return Collections.emptySet(); } return Collections.unmodifiableSet(namesToAlias); } public void setNamesToAlias(Set<String> names) { namesToAlias = names; } public void setModule(JSModule module) { this.module = module; } public JSModule getModule() { return module; } } /** * Interface for dealing with function declarations and function * expressions equally */ private static interface Function { /** Gets the name of the function */ public String getName(); /** Gets the function node */ public Node getFunctionNode(); /** Removes itself from the javascript */ public void remove(); public Node getDeclaringBlock(); } /** NamedFunction implementation of the Function interface */ private static class NamedFunction implements Function { private final Node fn; public NamedFunction(Node fn) { this.fn = fn; } public String getName() { return fn.getFirstChild().getString(); } public Node getFunctionNode() { return fn; } public void remove() { NodeUtil.removeChild(fn.getParent(), fn); } @Override public Node getDeclaringBlock() { return fn.getParent(); } } /** FunctionVar implementation of the Function interface */ private static class FunctionVar implements Function { private final Node var; public FunctionVar(Node var) { this.var = var; } public String getName() { return var.getFirstChild().getString(); } public Node getFunctionNode() { return var.getFirstChild().getFirstChild(); } public void remove() { NodeUtil.removeChild(var.getParent(), var); } @Override public Node getDeclaringBlock() { return var.getParent(); } } /** FunctionExpression implementation of the Function interface */ private static class FunctionExpression implements Function { private final Node fn; private final String fake

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>. */ enum InliningMode { /** * Directly replace the call expression. Only functions of meeting * strict preconditions can be inlined. */ DIRECT, /** * Replaces the call expression with a block of statements. Conditions * on the function are looser in mode, but stricter on the call site. */ BLOCK } /** Holds a reference to the call node of a function call */ static class Reference { final Node callNode; final JSModule module; final InliningMode mode; Reference(Node callNode, JSModule module, InliningMode mode){ this.callNode = callNode; this.module = module; this.mode = mode; } } /** * In order to estimate the cost of lining, we make the assumption that * Identifiers are reduced 2 characters. For the call arguments, the important * thing is that the cost is assumed to be the same in the call and the * function, so the actual length doesn't matter in most cases. */ private static final int NAME_COST_ESTIMATE = InlineCostEstimator.ESTIMATED_IDENTIFIER_COST; /** The cost of a argument separator (a comma). */ private static final int COMMA_COST = 1; /** The cost of the parentheses needed to make a call.*/ private static final int PAREN_COST = 2; /** * @param fnName The name of this function. This either the name of the * variable to which the function is assigned or the name from the FUNCTION * node. * @param fnNode The FUNCTION node of the function to inspect. * @return Whether the function node meets the minimum requirements for * inlining. */ boolean doesFunctionMeetMinimumRequirements( String fnName, Node fnNode) { Node block = NodeUtil.getFunctionBody(fnNode); // Don't inline recursive functions, nor functions that contain // 'this', 'arguments' references. if (NodeUtil.isNameReferenced(block, fnName)) { return false; } String fnRecursionName = fnNode.getFirstChild().getString(); if (fnRecursionName != null && !fnRecursionName.isEmpty() && !fnRecursionName.equals(fn

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> the // reference object and passing it in here. CallSiteType callSiteType = classifyCallSite(callNode); Preconditions.checkArgument(callSiteType != CallSiteType.UNSUPPORTED); // Store the name for the result. This will be used to // replace "return expr" statements with "resultName = expr" // to replace String resultName = null; boolean needsDefaultReturnResult = true; switch (callSiteType) { case SIMPLE_ASSIGNMENT: resultName = parent.getFirstChild().getString(); break; case VAR_DECL_SIMPLE_ASSIGNMENT: resultName = parent.getString(); break; case SIMPLE_CALL: resultName = null; // "foo()" doesn't need a result. needsDefaultReturnResult = false; break; case EXPRESSION: resultName = getUniqueResultName(); needsDefaultReturnResult = false; // The intermediary result already // has the default value. break; case DECOMPOSABLE_EXPRESSION: throw new IllegalStateException( "Decomposable expressions must decomposed before inlining."); default: throw new IllegalStateException("Unexpected call site type."); } boolean isCallInLoop = NodeUtil.isWithinLoop(callNode); FunctionToBlockMutator mutator = new FunctionToBlockMutator( compiler, this.safeNameIdSupplier); Node newBlock = mutator.mutate( fnName, fnNode, callNode, resultName, needsDefaultReturnResult, isCallInLoop); // TODO(nicksantos): Create a common mutation function that // can replace either a VAR name assignment, assignment expression or // a EXPR_RESULT. Node greatGrandParent = grandParent.getParent(); switch (callSiteType) { case VAR_DECL_SIMPLE_ASSIGNMENT: // Remove the call from the name node. parent.removeChild(parent.getFirstChild()); Preconditions.checkState(parent.getFirstChild() == null); // Add the call, after the VAR. greatGrandParent.addChildAfter(newBlock, grandParent); break; case SIMPLE_ASSIGNMENT: // The assignment is now part of the inline function so // replace it completely. Preconditions.checkState(NodeUtil.isExpressionNode(grandParent));

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> { if (NodeUtil.isFunctionObjectCall(callNode)) { // TODO(johnlenz): Support replace this with a value. Preconditions.checkNotNull(cArg); Preconditions.checkState(cArg.getType() == Token.THIS); cArg = cArg.getNext(); } else { // ".apply" call should be filtered before this. Preconditions.checkState(!NodeUtil.isFunctionObjectApply(callNode)); } } // FUNCTION NODE -> LP NODE: [ ARG1, ARG2, ... ] Node fnParam = NodeUtil.getFnParameters(fnNode).getFirstChild(); while (cArg != null || fnParam != null) { // For each named parameter check if a mutable argument use more than one. if (fnParam != null) { if (cArg != null) { // Check for arguments that are evaluated more than once. // Note: Unlike block inlining, there it is not possible that a // parameter reference will be in a loop. if (NodeUtil.mayEffectMutableState(cArg) && NodeUtil.getNameReferenceCount( block, fnParam.getString()) > 1) { return CanInlineResult.NO; } } // Move to the next name. fnParam = fnParam.getNext(); } // For every call argument check for side-effects, even if there // isn't a named parameter to match. if (cArg != null) { if (NodeUtil.mayHaveSideEffects(cArg)) { return CanInlineResult.NO; } cArg = cArg.getNext(); } } return CanInlineResult.YES; } /** * Parameter names will be name unique when at a later time. */ private String getUniqueResultName() { return "JSCompiler_inline_result" + ContextualRenamer.UNIQUE_ID_SEPARATOR + safeNameIdSupplier.get(); } /** * Determine if inlining the function is likely to reduce the code size. * @param namesToAlias */ boolean inliningLowersCost( JSModule fnModule, Node fnNode, Collection<? extends Reference> refs, Set<String> namesToAlias, boolean isRemovable, boolean referencesThis) { int referenceCount = refs.size();

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> NodeTraversal.AbstractPostOrderCallback { private final CodingConvention convention; PrepareAnnotations(AbstractCompiler compiler) { this.convention = compiler.getCodingConvention(); } public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.CALL: annotateCalls(n); break; case Token.FUNCTION: annotateFunctions(n, parent); annotateDispatchers(n, parent); break; case Token.NAME: case Token.STRING: annotateConstants(n, parent); break; case Token.OBJECTLIT: visitObjectLiteral(n); break; } } private void visitObjectLiteral(Node objlit) { Preconditions.checkState(objlit.getType() == Token.OBJECTLIT); for (Node key = objlit.getFirstChild(); key != null; key = key.getNext().getNext()) { Node value = key.getNext(); visitObjectLiteralKey(objlit, key, value); } } /** * Prepare the object literal keys. */ private void visitObjectLiteralKey(Node objlit, Node key, Node value) { normalizeObjectLitJsDocs(objlit, key, value); annotateObjLitConstants(objlit, key, value); } /** * There are two types of calls we are interested in calls without explicit * "this" values (what we are call "free" calls) and direct call to eval. */ private void annotateCalls(Node n) { Preconditions.checkState(n.getType() == Token.CALL); // Keep track of of the "this" context of a call. A call without an // explicit "this" is a free call. Node first = n.getFirstChild(); if (!NodeUtil.isGet(first)) { n.putBooleanProp(Node.FREE_CALL, true); } // Keep track of the context in which eval is called. It is important // to distinguish between "(0, eval)()" and "eval()". if (first.getType() == Token.NAME && "eval".equals(first.getString())) { first.putBooleanProp(Node.DIRECT_EVAL, true); } } /** * Translate dispatcher info into the property expected node.

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>key.getType() == Token.NAME || key.getType() == Token.STRING) { String name = key.getString(); if (convention.isConstantKey(name)) { key.putBooleanProp(Node.IS_CONSTANT_NAME, true); } } } /** * Annotate optional and var_arg function parameters. */ private void annotateFunctions(Node n, Node parent) { JSDocInfo fnInfo = NodeUtil.getFunctionInfo(n); // Compute which function parameters are optional and // which are var_args. Node args = n.getFirstChild().getNext(); for (Node arg = args.getFirstChild(); arg != null; arg = arg.getNext()) { String argName = arg.getString(); JSTypeExpression typeExpr = fnInfo == null ? null : fnInfo.getParameterType(argName); if (convention.isOptionalParameter(arg) || typeExpr != null && typeExpr.isOptionalArg()) { arg.putBooleanProp(Node.IS_OPTIONAL_PARAM, true); } if (convention.isVarArgsParameter(arg) || typeExpr != null && typeExpr.isVarArgs()) { arg.putBooleanProp(Node.IS_VAR_ARGS_PARAM, true); } } } } }

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> found, JSType required) { return MessageFormat.format(FOUND_REQUIRED, description, found, required); } /** * Given a node, get a human-readable name for the type of that node so * that will be easy for the programmer to find the original declaration. * * For example, if SubFoo's property "bar" might have the human-readable * name "Foo.prototype.bar". * * @param n The node. * @param dereference If true, the type of the node will be dereferenced * to an Object type, if possible. */ String getReadableJSTypeName(Node n, boolean dereference) { // If we're analyzing a GETPROP, the property may be inherited by the // prototype chain. So climb the prototype chain and find out where // the property was originally defined. if (n.getType() == Token.GETPROP) { ObjectType objectType = getJSType(n.getFirstChild()).dereference(); if (objectType != null) { String propName = n.getLastChild().getString(); while (objectType != null && !objectType.hasOwnProperty(propName)) { objectType = objectType.getImplicitPrototype(); } // Don't show complex function names or anonymous types. // Instead, try to get a human-readable type name. if (objectType != null && (objectType.getConstructor() != null || objectType.isFunctionPrototypeType())) { return objectType.toString() + "." + propName; } } } JSType type = getJSType(n); if (dereference) { ObjectType dereferenced = type.dereference(); if (dereferenced != null) { type = dereferenced; } } String qualifiedName = n.getQualifiedName(); if (type.isFunctionPrototypeType() || (type.toObjectType() != null && type.toObjectType().getConstructor() != null)) { return type.toString(); } else if (qualifiedName != null) { return qualifiedName; } else if (type instanceof FunctionType) { // Don't show complex function names. return "function"; } else { return type.toString(); } } /** * This method gets

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>)) { ungetChar(c2); this.string = getStringFromBuffer(); stringBufferTop = 0; return JsDocToken.STRING; } else { do { c1 = c2; c2 = getChar(); if (c1 == '.' && c2 == '<') { ungetChar(c2); ungetChar(c1); this.string = getStringFromBuffer(); stringBufferTop = 0; return JsDocToken.STRING; } else { if (isJSDocString(c2)) { addToString(c1); } else { ungetChar(c2); addToString(c1); this.string = getStringFromBuffer(); stringBufferTop = 0; return JsDocToken.STRING; } } } while (true); } } } } } /** * Gets the remaining JSDoc line without the {@link JsDocToken#EOL}, * {@link JsDocToken#EOF} or {@link JsDocToken#EOC}. */ @SuppressWarnings("fallthrough") String getRemainingJSDocLine() { int c; for (;;) { c = getChar(); switch (c) { case '*': if (peekChar() != '/') { addToString(c); break; } // fall through case EOF_CHAR: case '\n': ungetChar(c); this.string = getStringFromBuffer(); stringBufferTop = 0; return this.string; default: addToString(c); break; } } } final int getLineno() { return lineno; } final int getCharno() { return lineno == initLineno? initCharno + charno : charno; } final String getString() { return string; } final boolean eof() { return hitEOF; } private String getStringFromBuffer() { tokenEnd = cursor; return new String(stringBuffer, 0, stringBufferTop); } private void addToString(int c) { int N = stringBufferTop; if (N == stringBuffer.length) { char[] tmp = new char[stringBuffer.length * 2]; System.arraycopy(string

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>mayHaveSideEffects(operand)) { n.removeFirstChild(); reportCodeChange(); } break; case Token.NAME: String name = result.getString(); if (name.equals("undefined")) { n.removeFirstChild(); reportCodeChange(); } break; default: //Do nothing break; } } return n; } /** * Try to minimize NOT nodes such as !(x==y). * * Returns the replacement for n or the original if no change was made */ private Node tryMinimizeNot(Node n) { Node parent = n.getParent(); Node notChild = n.getFirstChild(); // negative operator of the current one : == -> != for instance. int complementOperator; switch (notChild.getType()) { case Token.EQ: complementOperator = Token.NE; break; case Token.NE: complementOperator = Token.EQ; break; case Token.SHEQ: complementOperator = Token.SHNE; break; case Token.SHNE: complementOperator = Token.SHEQ; break; // GT, GE, LT, LE are not handled in this because !(x<NaN) != x>=NaN. default: return n; } Node newOperator = n.removeFirstChild(); newOperator.setType(complementOperator); parent.replaceChild(n, newOperator); reportCodeChange(); return newOperator; } /** * Try turning IF nodes into smaller HOOKs * * Returns the replacement for n or the original if no replacement was * necessary. */ private Node tryMinimizeIf(Node n) { Node parent = n.getParent(); Node cond = n.getFirstChild(); /* If the condition is a literal, we'll let other * optimizations try to remove useless code. */ if (NodeUtil.isLiteralValue(cond, true)) { return n; } Node thenBranch = cond.getNext(); Node elseBranch = thenBranch.getNext(); if (elseBranch == null) { if (isExpressBlock(thenBranch)) { Node expr = getBlockExpression(thenBranch); if (isPropertyAssignmentInExpression(expr)) { // Keep opportunities for CollapseProperties

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> return expr; } } else if (NodeUtil.isCall(thenOp)) { // if(x)foo();else bar(); -> x?foo():bar() n.removeChild(cond); thenOp.detachFromParent(); elseOp.detachFromParent(); Node hookNode = new Node(Token.HOOK, cond, thenOp, elseOp) .copyInformationFrom(n); Node expr = NodeUtil.newExpr(hookNode); parent.replaceChild(n, expr); reportCodeChange(); return expr; } } return n; } boolean thenBranchIsVar = isVarBlock(thenBranch); boolean elseBranchIsVar = isVarBlock(elseBranch); // if(x)var y=1;else y=2 -> var y=x?1:2 if (thenBranchIsVar && elseBranchIsExpressionBlock && NodeUtil.isAssign(getBlockExpression(elseBranch).getFirstChild())) { Node var = getBlockVar(thenBranch); Node elseAssign = getBlockExpression(elseBranch).getFirstChild(); Node name1 = var.getFirstChild(); Node maybeName2 = elseAssign.getFirstChild(); if (name1.hasChildren() && maybeName2.getType() == Token.NAME && name1.getString().equals(maybeName2.getString())) { Node thenExpr = name1.removeChildren(); Node elseExpr = elseAssign.getLastChild().detachFromParent(); cond.detachFromParent(); Node hookNode = new Node(Token.HOOK, cond, thenExpr, elseExpr) .copyInformationFrom(n); var.detachFromParent(); name1.addChildrenToBack(hookNode); parent.replaceChild(n, var); reportCodeChange(); return var; } // if(x)y=1;else var y=2 -> var y=x?1:2 } else if (elseBranchIsVar && thenBranchIsExpressionBlock && NodeUtil.isAssign(getBlockExpression(thenBranch).getFirstChild())) { Node var = getBlockVar(elseBranch); Node thenAssign = getBlockExpression(thenBranch).getFirstChild(); Node maybeName1 = thenAssign.getFirstChild(); Node name2 = var.getFirstChild

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>(); if (name2.hasChildren() && maybeName1.getType() == Token.NAME && maybeName1.getString().equals(name2.getString())) { Node thenExpr = thenAssign.getLastChild().detachFromParent(); Node elseExpr = name2.removeChildren(); cond.detachFromParent(); Node hookNode = new Node(Token.HOOK, cond, thenExpr, elseExpr) .copyInformationFrom(n); var.detachFromParent(); name2.addChildrenToBack(hookNode); parent.replaceChild(n, var); reportCodeChange(); return var; } } return n; } /** * Try to remove duplicate statements from IF blocks. For example: * * if (a) { * x = 1; * return true; * } else { * x = 2; * return true; * } * * becomes: * * if (a) { * x = 1; * } else { * x = 2; * } * return true; * * @param n The IF node to examine. */ private void tryRemoveRepeatedStatements(Node n) { Preconditions.checkState(n.getType() == Token.IF); Node parent = n.getParent(); if (!NodeUtil.isStatementBlock(parent)) { // If the immediate parent is something like a label, we // can't move the statement, so bail. return; } Node cond = n.getFirstChild(); Node trueBranch = cond.getNext(); Node falseBranch = trueBranch.getNext(); Preconditions.checkNotNull(trueBranch); Preconditions.checkNotNull(falseBranch); while (true) { Node lastTrue = trueBranch.getLastChild(); Node lastFalse = falseBranch.getLastChild(); if (lastTrue == null || lastFalse == null || !areNodesEqualForInlining(lastTrue, lastFalse)) { break; } lastTrue.detachFromParent(); lastFalse.detachFromParent(); parent.addChildAfter(lastTrue, n); reportCodeChange(); } } /** * @return Whether the node is a block with a single statement that is * an expression.

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>n)) { parent.replaceChild(n, newNode); reportCodeChange(); return newNode; } return n; } private static final ImmutableSet<String> STANDARD_OBJECT_CONSTRUCTORS = // String, Number, and Boolean functions return non-object types, whereas // new String, new Number, and new Boolean return object types, so don't // include them here. ImmutableSet.of( "Object", "Array", "RegExp", "Error" ); /** * Fold "new Object()" to "Object()". */ private Node tryFoldStandardConstructors(Node n) { Preconditions.checkState(n.getType() == Token.NEW); // If name normalization has been run then we know that // new Object() does in fact refer to what we think it is // and not some custom-defined Object(). if (isASTNormalized()) { if (n.getFirstChild().getType() == Token.NAME) { String className = n.getFirstChild().getString(); if (STANDARD_OBJECT_CONSTRUCTORS.contains(className)) { n.setType(Token.CALL); reportCodeChange(); } } } return n; } /** * Replaces a new Array or Object node with an object literal, unless the * call to Array or Object is to a local function with the same name. */ private Node tryFoldLiteralConstructor(Node n) { Preconditions.checkArgument(n.getType() == Token.CALL || n.getType() == Token.NEW); Node constructorNameNode = n.getFirstChild(); Node newLiteralNode = null; // We require the AST to be normalized to ensure that, say, // Object() really refers to the built-in Object constructor // and not a user-defined constructor with the same name. if (isASTNormalized() && Token.NAME == constructorNameNode.getType()) { String className = constructorNameNode.getString(); if ("RegExp".equals(className)) { // "RegExp("boo", "g")" --> /boo/g return tryFoldRegularExpressionConstructor(n); } else { boolean constructorHasArgs = constructorNameNode.getNext() != null; if ("Object".equals(className) && !constructorHasArgs) {

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>TO_FOLD_WITHOUT_ARGS; } break; case (Token.ARRAYLIT): // "Array([args])" --> "[[args]]" action = FoldArrayAction.SAFE_TO_FOLD_WITH_ARGS; break; default: } } return action; } private Node tryFoldRegularExpressionConstructor(Node n) { Node parent = n.getParent(); Node constructor = n.getFirstChild(); Node pattern = constructor.getNext(); // e.g. ^foobar$ Node flags = null != pattern ? pattern.getNext() : null; // e.g. gi // Only run on normalized AST to make sure RegExp() is actually // the RegExp we expect (if the AST has been normalized then // other RegExp's will have been renamed to something like RegExp$1) if (!isASTNormalized()) { return n; } if (null == pattern || (null != flags && null != flags.getNext())) { // too few or too many arguments return n; } if (// is pattern folded pattern.getType() == Token.STRING // make sure empty pattern doesn't fold to // && !"".equals(pattern.getString()) // NOTE(nicksantos): Make sure that the regexp isn't longer than // 100 chars, or it blows up the regexp parser in Opera 9.2. && pattern.getString().length() < 100 && (null == flags || flags.getType() == Token.STRING) // don't escape patterns with unicode escapes since Safari behaves badly // (read can't parse or crashes) on regex literals with unicode escapes && !containsUnicodeEscape(pattern.getString())) { // Make sure that / is escaped, so that it will fit safely in /brackets/. // pattern is a string value with \\ and similar already escaped pattern = makeForwardSlashBracketSafe(pattern); Node regexLiteral; if (null == flags || "".equals(flags.getString())) { // fold to /foobar/ regexLiteral = new Node(Token.REGEXP, pattern); } else { // fold to /foobar/gi if (!areValidRegexpFlags(flags.getString())) { error(INVALID_REGULAR_EXPRESSION_FLAGS, flags); return n

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>; } if (!areSafeFlagsToFold(flags.getString())) { return n; } n.removeChild(flags); regexLiteral = new Node(Token.REGEXP, pattern, flags); } parent.replaceChild(n, regexLiteral); reportCodeChange(); return regexLiteral; } return n; } private static final Pattern REGEXP_FLAGS_RE = Pattern.compile("^[gmi]*$"); /** * are the given flags valid regular expression flags? * Javascript recognizes several suffix flags for regular expressions, * 'g' - global replace, 'i' - case insensitive, 'm' - multi-line. * They are case insensitive, and javascript does not recognize the extended * syntax mode, single-line mode, or expression replacement mode from perl5. */ private static boolean areValidRegexpFlags(String flags) { return REGEXP_FLAGS_RE.matcher(flags).matches(); } /** * are the given flags safe to fold? * We don't fold the regular expression if global ('g') flag is on, * because in this case it isn't really a constant: its 'lastIndex' * property contains the state of last execution, so replacing * 'new RegExp('foobar','g')' with '/foobar/g' may change the behavior of * the program if the RegExp is used inside a loop, for example. */ private static boolean areSafeFlagsToFold(String flags) { return flags.indexOf('g') < 0; } /** * returns a string node that can safely be rendered inside /brackets/. */ private static Node makeForwardSlashBracketSafe(Node n) { String s = n.getString(); // sb contains everything in s[0:pos] StringBuilder sb = null; int pos = 0; for (int i = 0; i < s.length(); ++i) { switch (s.charAt(i)) { case '\\': // skip over the next char after a '\\'. ++i; break; case '/': // escape it if (null == sb) { sb = new StringBuilder(s.length() + 16); } sb.append(s, pos, i).append('\\'); pos = i

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>Names = preserveFunctionExpressionNames; } /** * Traverses the root, removing all unused variables. Multiple traversals * may occur to ensure all unused variables are removed. */ public void process(Node externs, Node root) { traverseAndRemoveUnusedReferences(root); } /** * Traverses a node recursively. Call this once per pass. */ private void traverseAndRemoveUnusedReferences(Node root) { Scope scope = new SyntacticScopeCreator(compiler).createScope(root, null); traverseNode(root, null, scope); if (removeGlobals) { collectMaybeUnreferencedVars(scope); } interpretAssigns(); removeUnreferencedVars(); for (Scope fnScope : allFunctionScopes) { removeUnreferencedFunctionArgs(fnScope); } } /** * Traverses everything in the current scope and marks variables that * are referenced. * * During traversal, we identify subtrees that will only be * referenced if their enclosing variables are referenced. Instead of * traversing those subtrees, we create a continuation for them, * and traverse them lazily. */ private void traverseNode(Node n, Node parent, Scope scope) { int type = n.getType(); Var var = null; switch (type) { case Token.FUNCTION: // If this function is a removable var, then create a continuation // for it instead of traversing immediately. if (NodeUtil.isFunctionDeclaration(n)) { var = scope.getVar(n.getFirstChild().getString()); } if (var != null && isRemovableVar(var)) { continuations.put(var, new Continuation(n, scope)); } else { traverseFunction(n, scope); } return; case Token.ASSIGN: Assign maybeAssign = Assign.maybeCreateAssign(n); if (maybeAssign != null) { // Put this in the assign map. It might count as a reference, // but we won't know that until we have an index of all assigns. var = scope.getVar(maybeAssign.nameNode.getString()); if (var != null) { assignsByVar.put(var, maybeAssign); assignsByNode.put(maybeAssign.name

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>Node, maybeAssign); if (isRemovableVar(var) && !maybeAssign.mayHaveSecondarySideEffects) { // If the var is unreferenced and performing this assign has // no secondary side effects, then we can create a continuation // for it instead of traversing immediately. continuations.put(var, new Continuation(n, scope)); return; } } } break; case Token.NAME: var = scope.getVar(n.getString()); if (parent.getType() == Token.VAR) { Node value = n.getFirstChild(); if (value != null && var != null && isRemovableVar(var) && !NodeUtil.mayHaveSideEffects(value)) { // If the var is unreferenced and creating its value has no side // effects, then we can create a continuation for it instead // of traversing immediately. continuations.put(var, new Continuation(n, scope)); return; } } else { // All name references that aren't declarations or assigns // are references to other vars. if (var != null) { // If that var hasn't already been marked referenced, then // start tracking it. If this is an assign, do nothing // for now. if (isRemovableVar(var)) { if (!assignsByNode.containsKey(n)) { markReferencedVar(var); } } else { markReferencedVar(var); } } } break; } for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { traverseNode(c, n, scope); } } private boolean isRemovableVar(Var var) { // Global variables are off-limits if the user might be using them. if (!removeGlobals && var.isGlobal()) { return false; } // Referenced variables are off-limits. if (referenced.contains(var)) { return false; } // Exported variables are off-limits. if (compiler.getCodingConvention().isExported(var.getName())) { return false; } return true; } /** * Traverses a function, which creates a new scope in javascript. * *

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> Note that CATCH blocks also create a new scope, but only for the * catch variable. Declarations within the block actually belong to the * enclosing scope. Because we don't remove catch variables, there's * no need to treat CATCH blocks differently like we do functions. */ private void traverseFunction(Node n, Scope parentScope) { Preconditions.checkState(n.getChildCount() == 3); Preconditions.checkState(n.getType() == Token.FUNCTION); final Node body = n.getLastChild(); Preconditions.checkState(body.getNext() == null && body.getType() == Token.BLOCK); Scope fnScope = new SyntacticScopeCreator(compiler).createScope(n, parentScope); traverseNode(body, n, fnScope); collectMaybeUnreferencedVars(fnScope); allFunctionScopes.add(fnScope); } /** * For each variable in this scope that we haven't found a reference * for yet, add it to the list of variables to check later. */ private void collectMaybeUnreferencedVars(Scope scope) { for (Iterator<Var> it = scope.getVars(); it.hasNext(); ) { Var var = it.next(); if (isRemovableVar(var)) { maybeUnreferenced.add(var); } } } /** * Removes unreferenced arguments from a function declaration. * * @param fnScope The scope inside the function */ private void removeUnreferencedFunctionArgs(Scope fnScope) { // Strip unreferenced args off the end of the function declaration. Node function = fnScope.getRootNode(); Preconditions.checkState(function.getType() == Token.FUNCTION); Node argList = function.getFirstChild().getNext(); Node lastArg; while ((lastArg = argList.getLastChild()) != null) { Var var = fnScope.getVar(lastArg.getString()); if (!referenced.contains(var)) { if (var == null) { throw new IllegalStateException( "Function parameter not declared in scope: " + lastArg.getString()); } argList.removeChild(lastArg); compiler.reportCodeChange(); } else { break; } } } /** * Look at all the property assigns to all variables. *

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>(); // var foo = (a = b); // In the first two cases, the sides of the assignment have side-effects. // In the last one, the result of the assignment is used, so we // are conservative and assume that it may be used in a side-effecting // way. final boolean mayHaveSecondarySideEffects; Assign(Node assignNode, Node nameNode, boolean isPropertyAssign) { Preconditions.checkState(NodeUtil.isAssignmentOp(assignNode)); this.assignNode = assignNode; this.nameNode = nameNode; this.isPropertyAssign = isPropertyAssign; this.mayHaveSecondarySideEffects = assignNode.getParent().getType() != Token.EXPR_RESULT || NodeUtil.mayHaveSideEffects(assignNode.getFirstChild()) || NodeUtil.mayHaveSideEffects(assignNode.getLastChild()); } /** * If this is an assign to a variable or its property, return it. * Otherwise, return null. */ static Assign maybeCreateAssign(Node assignNode) { Preconditions.checkState(NodeUtil.isAssignmentOp(assignNode)); // Skip one level of GETPROPs or GETELEMs. // // Don't skip more than one level, because then we get into // situations where assigns to properties of properties will always // trigger side-effects, and the variable they're on cannot be removed. boolean isPropAssign = false; Node current = assignNode.getFirstChild(); if (NodeUtil.isGet(current)) { current = current.getFirstChild(); isPropAssign = true; if (current.getType() == Token.GETPROP && current.getLastChild().getString().equals("prototype")) { // Prototype properties sets should be considered like normal // property sets. current = current.getFirstChild(); } } if (current.getType() == Token.NAME) { return new Assign(assignNode, current, isPropAssign); } return null; } /** * Replace the current assign with its right hand side. */ void remove() { Node parent = assignNode.getParent(); if (mayHaveSecondarySideEffects) { Node replacement = assignNode.getLastChild().detachFromParent(); // Aggregate any expressions in GETE

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>DuplicateDeclarations runs after // MakeDeclaredNamesUnique in order for catch block exception names to be // handled properly. Specifically, catch block exception names are // only valid within the catch block, but our currect Scope logic // has no concept of this and includes it in the containing function // (or global scope). MakeDeclaredNamesUnique makes the catch exception // names unique so that removeDuplicateDeclarations() will properly handle // cases where a function scope variable conflict with a exception name: // function f() { // try {throw 0;} catch(e) {e; /* catch scope 'e'*/} // var e = 1; // f scope 'e' // } // otherwise 'var e = 1' would be rewritten as 'e = 1'. // TODO(johnlenz): Introduce a seperate scope for catch nodes. removeDuplicateDeclarations(externs, root); new PropogateConstantAnnotations(compiler, assertOnChange) .process(externs, root); compiler.setNormalized(); } public static class PropogateConstantAnnotations extends AbstractPostOrderCallback implements CompilerPass { private final AbstractCompiler compiler; private final boolean assertOnChange; public PropogateConstantAnnotations( AbstractCompiler compiler, boolean forbidChanges) { this.compiler = compiler; this.assertOnChange = forbidChanges; } @Override public void process(Node externs, Node root) { new NodeTraversal(compiler, this).traverseRoots(externs, root); } @Override public void visit(NodeTraversal t, Node n, Node parent) { // Note: Constant properties annotations are not propagated. if (n.getType() == Token.NAME) { if (n.getString().isEmpty()) { return; } JSDocInfo info = null; // Find the JSDocInfo for a top level variable. Var var = t.getScope().getVar(n.getString()); if (var != null) { info = var.getJSDocInfo(); } if ((info != null && info.isConstant()) && !n.getBooleanProp(Node.IS_CONSTANT_NAME)) { n.putBooleanProp(Node.IS_CONSTANT_NAME, true); if (assertOnChange

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>) { String name = n.getString(); throw new IllegalStateException( "Unexpected const change.\n" + " name: "+ name + "\n" + " gramps:" + n.getParent().getParent().toStringTree()); } // Even though the AST has changed (an annotation was added), // the annotations are not compared so don't report the change. // reportCodeChange("constant annotation"); } } } } /** * Walk the AST tree and verify that constant names are used consistently. */ static class VerifyConstants extends AbstractPostOrderCallback implements CompilerPass { final private AbstractCompiler compiler; final private boolean checkUserDeclarations; VerifyConstants(AbstractCompiler compiler, boolean checkUserDeclarations) { this.compiler = compiler; this.checkUserDeclarations = checkUserDeclarations; } @Override public void process(Node externs, Node root) { Node externsAndJs = root.getParent(); Preconditions.checkState(externsAndJs != null); Preconditions.checkState(externsAndJs.hasChild(externs)); NodeTraversal.traverseRoots( compiler, Lists.newArrayList(externs, root), this); } private Map<String,Boolean> constantMap = Maps.newHashMap(); @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.NAME) { String name = n.getString(); if (n.getString().isEmpty()) { return; } boolean isConst = n.getBooleanProp(Node.IS_CONSTANT_NAME); if (checkUserDeclarations) { boolean expectedConst = false; CodingConvention convention = compiler.getCodingConvention(); if (NodeUtil.isConstantName(n) || NodeUtil.isConstantByConvention(convention, n, parent)) { expectedConst = true; } else { expectedConst = false; JSDocInfo info = null; Var var = t.getScope().getVar(n.getString()); if (var != null) { info = var.getJSDocInfo(); } if (info != null && info.isConstant()) { expectedConst = true; } else { expectedConst = false; } } if (expectedConst) {

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> return tryFoldAdd(subtree, left, right); case Token.SUB: case Token.MUL: case Token.DIV: return tryFoldArithmetic(subtree, left, right); case Token.LT: case Token.GT: case Token.LE: case Token.GE: case Token.EQ: case Token.NE: case Token.SHEQ: case Token.SHNE: return tryFoldComparison(subtree, left, right); default: return subtree; } } /** * Folds 'typeof(foo)' if foo is a literal, e.g. * typeof("bar") --> "string" * typeof(6) --> "number" */ private Node tryFoldTypeof(Node originalTypeofNode) { Preconditions.checkArgument(originalTypeofNode.getType() == Token.TYPEOF); Node argumentNode = originalTypeofNode.getFirstChild(); if (argumentNode == null || !NodeUtil.isLiteralValue(argumentNode, true)) { return originalTypeofNode; } String typeNameString = null; switch (argumentNode.getType()) { case Token.FUNCTION: typeNameString = "function"; break; case Token.STRING: typeNameString = "string"; break; case Token.NUMBER: typeNameString = "number"; break; case Token.TRUE: case Token.FALSE: typeNameString = "boolean"; break; case Token.NULL: case Token.OBJECTLIT: case Token.ARRAYLIT: typeNameString = "object"; break; case Token.VOID: typeNameString = "undefined"; break; case Token.NAME: // We assume here that programs don't change the value of the // keyword undefined to something other than the value undefined. if ("undefined".equals(argumentNode.getString())) { typeNameString = "undefined"; } break; } if (typeNameString != null) { Node newNode = Node.newString(typeNameString); originalTypeofNode.getParent().replaceChild(originalTypeofNode, newNode); reportCodeChange(); return newNode; } return originalTypeofNode; } private Node tryFoldUnaryOperator(Node n) { Preconditions.checkState(n.

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>hasOneChild()); Node left = n.getFirstChild(); Node parent = n.getParent(); if (left == null) { return n; } TernaryValue leftVal = NodeUtil.getBooleanValue(left); if (leftVal == TernaryValue.UNKNOWN) { return n; } switch (n.getType()) { case Token.NOT: int result = leftVal.toBoolean(true) ? Token.FALSE : Token.TRUE; Node replacementNode = new Node(result); parent.replaceChild(n, replacementNode); reportCodeChange(); return replacementNode; case Token.NEG: try { if (left.getType() == Token.NAME) { if (left.getString().equals("Infinity")) { // "-Infinity" is valid and a literal, don't modify it. return n; } else if (left.getString().equals("NaN")) { // "-NaN" is "NaN". n.removeChild(left); parent.replaceChild(n, left); reportCodeChange(); return left; } } double negNum = -left.getDouble(); Node negNumNode = Node.newNumber(negNum); parent.replaceChild(n, negNumNode); reportCodeChange(); return negNumNode; } catch (UnsupportedOperationException ex) { // left is not a number node, so do not replace, but warn the // user because they can't be doing anything good error(NEGATING_A_NON_NUMBER_ERROR, left); return n; } case Token.BITNOT: try { double val = left.getDouble(); if (val >= Integer.MIN_VALUE && val <= Integer.MAX_VALUE) { int intVal = (int) val; if (intVal == val) { Node notIntValNode = Node.newNumber(~intVal); parent.replaceChild(n, notIntValNode); reportCodeChange(); return notIntValNode; } else { error(FRACTIONAL_BITWISE_OPERAND, left); return n; } } else { error(BITWISE_OPERAND_OUT_OF_RANGE, left); return n; } } catch (UnsupportedOperationException ex) { // left

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> is not a number node, so do not replace, but warn the // user because they can't be doing anything good error(NEGATING_A_NON_NUMBER_ERROR, left); return n; } default: return n; } } /** * Try to fold {@code left instanceof right} into {@code true} * or {@code false}. */ private Node tryFoldInstanceof(Node n, Node left, Node right) { Preconditions.checkArgument(n.getType() == Token.INSTANCEOF); // TODO(johnlenz) Use type information if available to fold // instanceof. if (NodeUtil.isLiteralValue(left, true) && !mayHaveSideEffects(right)) { Node replacementNode = null; if (NodeUtil.isImmutableValue(left)) { // Non-object types are never instances. replacementNode = new Node(Token.FALSE); } else if (right.getType() == Token.NAME && "Object".equals(right.getString())) { replacementNode = new Node(Token.TRUE); } if (replacementNode != null) { n.getParent().replaceChild(n, replacementNode); reportCodeChange(); return replacementNode; } } return n; } private Node tryFoldAssign(Node n, Node left, Node right) { Preconditions.checkArgument(n.getType() == Token.ASSIGN); // Tries to convert x = x + y -> x += y; if (!right.hasChildren() || right.getFirstChild().getNext() != right.getLastChild()) { // RHS must have two children. return n; } if (mayHaveSideEffects(left)) { return n; } Node leftChild = right.getFirstChild(); if (!areNodesEqualForInlining(left, leftChild)) { return n; } int newType = -1; switch (right.getType()) { case Token.ADD: newType = Token.ASSIGN_ADD; break; case Token.BITAND: newType = Token.ASSIGN_BITAND; break; case Token.BITOR: newType = Token.ASSIGN_BITOR; break; case Token.BITXOR: newType = Token.ASSIGN

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> result = lvalInt << rvalInt; break; case Token.RSH: result = lvalInt >> rvalInt; break; case Token.URSH: // JavaScript handles zero shifts on signed numbers differently than // Java as an Java int can not represent the unsigned 32-bit number // where JavaScript can so use a long here. long lvalLong = lvalInt & 0xffffffffL; result = lvalLong >>> rvalInt; break; default: throw new AssertionError("Unknown shift operator: " + Node.tokenToName(n.getType())); } Node newNumber = Node.newNumber(result); n.getParent().replaceChild(n, newNumber); reportCodeChange(); return newNumber; } return n; } /** * Try to fold comparison nodes, e.g == */ @SuppressWarnings("fallthrough") private Node tryFoldComparison(Node n, Node left, Node right) { if (!NodeUtil.isLiteralValue(left, false) || !NodeUtil.isLiteralValue(right, false)) { // We only handle non-literal operands for LT and GT. if (n.getType() != Token.GT && n.getType() != Token.LT) { return n; } } int op = n.getType(); boolean result; // TODO(johnlenz): Use the JSType to compare nodes of different types. boolean rightLiteral = NodeUtil.isLiteralValue(right, false); boolean undefinedRight = ((Token.NAME == right.getType() && right.getString().equals("undefined")) || (Token.VOID == right.getType() && NodeUtil.isLiteralValue(right.getFirstChild(), false))); switch (left.getType()) { case Token.VOID: if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) { return n; } else if (!rightLiteral) { return n; } else { boolean nullRight = (Token.NULL == right.getType()); boolean equivalent = undefinedRight || nullRight; switch (op) { case Token.EQ: // undefined is only equal to result = equivalent; break; case Token.NE: result = !equivalent; break;

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> case Token.SHEQ: result = undefinedRight; break; case Token.SHNE: result = !undefinedRight; break; case Token.LT: case Token.GT: case Token.LE: case Token.GE: result = false; break; default: return n; } } break; case Token.NULL: if (undefinedRight) { result = (op == Token.EQ); break; } // fall through case Token.TRUE: case Token.FALSE: if (undefinedRight) { result = false; break; } // fall through case Token.THIS: int tt = right.getType(); if (tt != Token.THIS && tt != Token.TRUE && tt != Token.FALSE && tt != Token.NULL) { return n; } switch (op) { case Token.SHEQ: case Token.EQ: result = left.getType() == right.getType(); break; case Token.SHNE: case Token.NE: result = left.getType() != right.getType(); break; default: return n; // we only handle == and != here } break; case Token.STRING: if (undefinedRight) { result = false; break; } if (Token.STRING != right.getType()) { return n; // Only eval if they are the same type } switch (op) { case Token.SHEQ: case Token.EQ: result = left.getString().equals(right.getString()); break; case Token.SHNE: case Token.NE: result = !left.getString().equals(right.getString()); break; default: return n; // we only handle == and != here } break; case Token.NUMBER: if (undefinedRight) { result = false; break; } if (Token.NUMBER != right.getType()) { return n; // Only eval if they are the same type } double lv = left.getDouble(); double rv = right.getDouble(); switch (op) { case Token.SHEQ: case Token.EQ: result = lv == rv; break;

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> case Token.SHNE: case Token.NE: result = lv != rv; break; case Token.LE: result = lv <= rv; break; case Token.LT: result = lv < rv; break; case Token.GE: result = lv >= rv; break; case Token.GT: result = lv > rv; break; default: return n; // don't handle that op } break; case Token.NAME: if (rightLiteral) { boolean undefinedLeft = (left.getString().equals("undefined")); if (undefinedLeft) { boolean nullRight = (Token.NULL == right.getType()); boolean equivalent = undefinedRight || nullRight; switch (op) { case Token.EQ: // undefined is only equal to result = equivalent; break; case Token.NE: result = !equivalent; break; case Token.SHEQ: result = undefinedRight; break; case Token.SHNE: result = !undefinedRight; break; case Token.LT: case Token.GT: case Token.LE: case Token.GE: result = false; break; default: return n; } break; } } if (Token.NAME != right.getType()) { return n; // Only eval if they are the same type } String ln = left.getString(); String rn = right.getString(); if (!ln.equals(rn)) { return n; // Not the same value name. } switch (op) { // If we knew the named value wouldn't be NaN, it would be nice // to handle EQ,NE,LE,GE,SHEQ, and SHNE. case Token.LT: case Token.GT: result = false; break; default: return n; // don't handle that op } break; default: // assert, this should cover all consts return n; } Node newNode = new Node(result ? Token.TRUE : Token.FALSE); n.getParent().replaceChild(n, newNode); reportCodeChange(); return newNode; } /** * Try to fold away unnecessary object instantiation. * e.g. this[

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>new String('eval')] -> this.eval */ private Node tryFoldCtorCall(Node n) { Preconditions.checkArgument(n.getType() == Token.NEW); // we can remove this for GETELEM calls (anywhere else?) if (inForcedStringContext(n)) { return tryFoldInForcedStringContext(n); } return n; } /** Returns whether this node must be coerced to a string. */ private boolean inForcedStringContext(Node n) { return n.getParent().getType() == Token.GETELEM && n.getParent().getLastChild() == n; } private Node tryFoldInForcedStringContext(Node n) { // For now, we only know how to fold ctors. Preconditions.checkArgument(n.getType() == Token.NEW); Node objectType = n.getFirstChild(); if (objectType.getType() != Token.NAME) { return n; } if (objectType.getString().equals("String")) { Node value = objectType.getNext(); String stringValue = null; if (value == null) { stringValue = ""; } else { if (!NodeUtil.isImmutableValue(value)) { return n; } stringValue = NodeUtil.getStringValue(value); } if (stringValue == null) { return n; } Node parent = n.getParent(); Node newString = Node.newString(stringValue); parent.replaceChild(n, newString); newString.copyInformationFrom(parent); reportCodeChange(); return newString; } return n; } private Node tryFoldKnownMethods(Node subtree) { // For now we only support .join() and .indexOf() subtree = tryFoldStringJoin(subtree); if (subtree.getType() == Token.CALL) { subtree = tryFoldStringIndexOf(subtree); } return subtree; } /** * Try to evaluate String.indexOf/lastIndexOf: * "abcdef".indexOf("bc") -> 1 * "abcdefbc".indexOf("bc", 3) -> 6 */ private Node tryFoldStringIndexOf(Node n) { Preconditions.checkArgument(n.getType() == Token.CALL);

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> Node left = n.getFirstChild(); if (left == null) { return n; } Node right = left.getNext(); if (right == null) { return n; } if (!NodeUtil.isGetProp(left) || !NodeUtil.isImmutableValue(right)) { return n; } Node lstringNode = left.getFirstChild(); Node functionName = lstringNode.getNext(); if ((lstringNode.getType() != Token.STRING) || (!functionName.getString().equals("indexOf") && !functionName.getString().equals("lastIndexOf"))) { return n; } String lstring = NodeUtil.getStringValue(lstringNode); boolean isIndexOf = functionName.getString().equals("indexOf"); Node firstArg = right; Node secondArg = right.getNext(); String searchValue = NodeUtil.getStringValue(firstArg); // searchValue must be a valid string. if (searchValue == null) { return n; } int fromIndex = isIndexOf ? 0 : lstring.length(); if (secondArg != null) { // Third-argument and non-numeric second arg are problematic. Discard. if ((secondArg.getNext() != null) || (secondArg.getType() != Token.NUMBER)) { return n; } else { fromIndex = (int) secondArg.getDouble(); } } int indexVal = isIndexOf ? lstring.indexOf(searchValue, fromIndex) : lstring.lastIndexOf(searchValue, fromIndex); Node newNode = Node.newNumber(indexVal); n.getParent().replaceChild(n, newNode); reportCodeChange(); return newNode; } /** * Try to fold an array join: ['a', 'b', 'c'].join('') -> 'abc'; */ private Node tryFoldStringJoin(Node n) { Node left = n.getFirstChild(); if (left == null) { return n; } Node right = left.getNext(); if (right == null) { return n; } if (!NodeUtil.isGetProp(left) || !NodeUtil.isImmutableValue(right)) { return n; } Node arrayNode = left.getFirstChild();

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> Node functionName = arrayNode.getNext(); if ((arrayNode.getType() != Token.ARRAYLIT) || !functionName.getString().equals("join")) { return n; } String joinString = NodeUtil.getStringValue(right); List<Node> arrayFoldedChildren = Lists.newLinkedList(); StringBuilder sb = null; int foldedSize = 0; Node prev = null; Node elem = arrayNode.getFirstChild(); // Merges adjacent String nodes. while (elem != null) { if (NodeUtil.isImmutableValue(elem)) { if (sb == null) { sb = new StringBuilder(); } else { sb.append(joinString); } sb.append(NodeUtil.getStringValue(elem)); } else { if (sb != null) { Preconditions.checkNotNull(prev); // + 2 for the quotes. foldedSize += sb.length() + 2; arrayFoldedChildren.add( Node.newString(sb.toString()).copyInformationFrom(prev)); sb = null; } foldedSize += InlineCostEstimator.getCost(elem); arrayFoldedChildren.add(elem); } prev = elem; elem = elem.getNext(); } if (sb != null) { Preconditions.checkNotNull(prev); // + 2 for the quotes. foldedSize += sb.length() + 2; arrayFoldedChildren.add( Node.newString(sb.toString()).copyInformationFrom(prev)); } // one for each comma. foldedSize += arrayFoldedChildren.size() - 1; int originalSize = InlineCostEstimator.getCost(n); switch (arrayFoldedChildren.size()) { case 0: Node emptyStringNode = Node.newString(""); n.getParent().replaceChild(n, emptyStringNode); reportCodeChange(); return emptyStringNode; case 1: Node foldedStringNode = arrayFoldedChildren.remove(0); if (foldedSize > originalSize) { return n; } arrayNode.detachChildren(); if (foldedStringNode.getType() != Token.STRING) { // If the Node

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>elem); n.getParent().replaceChild(n, elem); reportCodeChange(); return elem; } return n; } /** * Try to fold array-length. e.g [1, 2, 3].length ==> 3, [x, y].length ==> 2 */ private Node tryFoldGetProp(Node n, Node left, Node right) { Preconditions.checkArgument(n.getType() == Token.GETPROP); if (right.getType() == Token.STRING && right.getString().equals("length")) { int knownLength = -1; switch (left.getType()) { case Token.ARRAYLIT: if (mayHaveSideEffects(left)) { // Nope, can't fold this, without handling the side-effects. return n; } knownLength = left.getChildCount(); break; case Token.STRING: knownLength = left.getString().length(); break; default: // Not a foldable case, forget it. return n; } Preconditions.checkState(knownLength != -1); Node lengthNode = Node.newNumber(knownLength); n.getParent().replaceChild(n, lengthNode); reportCodeChange(); return lengthNode; } return n; } }

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> next; } if (n.isSyntheticBlock() || n.getParent() == null) { return n; } // Try to remove the block. if (NodeUtil.tryMergeBlock(n)) { reportCodeChange(); return null; } return n; } // TODO(johnlenz): Consider moving this to a separate peephole pass. /** * Attempt to replace the condition of if or hook immediately that is a * reference to a name that is assigned immediately before. */ private void tryOptimizeConditionalAfterAssign(Node n) { Node next = n.getNext(); // Look for patterns like the following and replace the if-condition with // a constant value so it can later be folded: // var a = /a/; // if (a) {foo(a)} // or // a = 0; // a ? foo(a) : c; // or // a = 0; // a || foo(a); // or // a = 0; // a && foo(a) // // TODO(johnlenz): This would be better handled by control-flow sensitive // constant propagation. As the other case that I want to handle is: // i=0; for(;i<0;i++){} // as right now nothing facilitates removing a loop like that. // This is here simply to remove the cruft left behind goog.userAgent and // similar cases. if (isSimpleAssignment(n) && isConditionalStatement(next)) { Node lhsAssign = getSimpleAssignmentName(n); Node condition = getConditionalStatementCondition(next); if (NodeUtil.isName(lhsAssign) && NodeUtil.isName(condition) && lhsAssign.getString().equals(condition.getString())) { Node rhsAssign = getSimpleAssignmentValue(n); TernaryValue value = NodeUtil.getExpressionBooleanValue(rhsAssign); if (value != TernaryValue.UNKNOWN) { int replacementConditionNodeType = (value.toBoolean(true)) ? Token.TRUE : Token.FALSE; condition.getParent().replaceChild(condition, new Node(replacementConditionNodeType)); reportCodeChange(); } }

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> n, Node parent) { if (n.getType() != Token.NAME) { return; } String varName = n.getString(); // Only a function can have an empty name. if (varName.isEmpty()) { Preconditions.checkState(NodeUtil.isFunction(parent)); // A function declaration with an empty name passes Rhino, // but is supposed to be a syntax error according to the spec. if (!NodeUtil.isFunctionExpression(parent)) { t.report(n, INVALID_FUNCTION_DECL); } return; } // Check if this is a declaration for a var that has been declared // elsewhere. If so, mark it as a duplicate. if ((parent.getType() == Token.VAR || NodeUtil.isFunctionDeclaration(parent)) && varsToDeclareInExterns.contains(varName)) { createSynthesizedExternVar(varName); parent.addSuppression("duplicate"); } // Check that the var has been declared. Scope scope = t.getScope(); Scope.Var var = scope.getVar(varName); if (var == null) { if (NodeUtil.isFunctionExpression(parent)) { // e.g. [ function foo() {} ], it's okay if "foo" isn't defined in the // current scope. } else { // The extern checks are stricter, don't report a second error. if (!strictExternCheck || !t.getInput().isExtern()) { t.report(n, UNDEFINED_VAR_ERROR, varName); } if (sanityCheck) { throw new IllegalStateException("Unexpected variable " + varName); } else { createSynthesizedExternVar(varName); scope.getGlobalScope().declare(varName, n, null, getSynthesizedExternsInput()); } } return; } CompilerInput currInput = t.getInput(); CompilerInput varInput = var.input; if (currInput == varInput || currInput == null || varInput == null) { // The variable was defined in the same file. This is fine. return; } // Check module dependencies. JSModule currModule = currInput.getModule(); JSModule varModule = varInput.getModule(); JSModuleGraph moduleGraph =

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.NAME) { switch (parent.getType()) { case Token.VAR: case Token.FUNCTION: case Token.LP: // These are okay. break; case Token.GETPROP: if (n == parent.getFirstChild()) { Scope scope = t.getScope(); Scope.Var var = scope.getVar(n.getString()); if (var == null) { t.report(n, UNDEFINED_EXTERN_VAR_ERROR, n.getString()); varsToDeclareInExterns.add(n.getString()); } } break; default: t.report(n, NAME_REFERENCE_IN_EXTERNS_ERROR, n.getString()); Scope scope = t.getScope(); Scope.Var var = scope.getVar(n.getString()); if (var == null) { varsToDeclareInExterns.add(n.getString()); } break; } } } } /** Lazily create a "new" externs input for undeclared variables. */ private CompilerInput getSynthesizedExternsInput() { if (synthesizedExternsInput == null) { synthesizedExternsInput = compiler.newExternInput("{SyntheticVarsDeclar}"); } return synthesizedExternsInput; } /** Lazily create a "new" externs root for undeclared variables. */ private Node getSynthesizedExternsRoot() { if (synthesizedExternsRoot == null) { CompilerInput synthesizedExterns = getSynthesizedExternsInput(); synthesizedExternsRoot = synthesizedExterns.getAstRoot(compiler); } return synthesizedExternsRoot; } }

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>.pseudoNameMap = Maps.newHashMap(); } else { this.pseudoNameMap = null; } this.prevUsedRenameMap = prevUsedRenameMap; this.reservedCharacters = reservedCharacters; if (reservedNames == null) { this.reservedNames = Sets.newHashSet(); } else { this.reservedNames = Sets.newHashSet(reservedNames); } } /** * Iterate through the nodes, collect all the NAME nodes that need to be * renamed, and count how many times each variable name is referenced. * * There are 2 passes: * - externs: keep track of the global vars in the externNames_ map. * - source: keep track of all name references in globalNameNodes_, and * localNameNodes_. * * To get shorter local variable renaming, we rename local variables to a * temporary name "LOCAL_VAR_PREFIX + index" where index is the index of the * variable declared in the local scope stack. * e.g. * Foo(fa, fb) { * var c = function(d, e) { return fa; } * } * The indexes are: fa:0, fb:1, c:2, d:3, e:4 * * In that way, local variable names are reused in each global function. * e.g. the final code might look like * function x(a,b) { ... } * function y(a,b,c) { ... } */ class ProcessVars extends AbstractPostOrderCallback { private final boolean isExternsPass_; ProcessVars(boolean isExterns) { isExternsPass_ = isExterns; } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() != Token.NAME) { return; } String name = n.getString(); // Ignore anonymous functions if (name.length() == 0) { return; } // Is this local or Global? Scope.Var var = t.getScope().getVar(name); boolean local = (var != null) && var.isLocal(); // Are we renaming global variables? if (!local && localRenamingOnly)

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> getNewGlobalName(Node n) { String oldName = n.getString(); Assignment a = assignments.get(oldName); if (a.newName != null && !a.newName.equals(oldName)) { if (pseudoNameMap != null) { return pseudoNameMap.get(n); } return a.newName; } else { return null; } } private String getNewLocalName(Node n) { String oldTempName = n.getString(); Assignment a = assignments.get(oldTempName); if (!a.newName.equals(oldTempName)) { if (pseudoNameMap != null) { return pseudoNameMap.get(n); } return a.newName; } return null; } private void recordPseudoName(Node n) { // Variable names should be in a different name space than // property pseudo names. pseudoNameMap.put(n, '$' + n.getString() + "$$" ); } /** * Runs through the assignments and reuses as many names as possible from the * previously used variable map. Updates reservedNames with the set of names * that were reused. */ private void reusePreviouslyUsedVariableMap() { for (Assignment a : assignments.values()) { String prevNewName = prevUsedRenameMap.lookupNewName(a.oldName); if (prevNewName == null || reservedNames.contains(prevNewName)) { continue; } if (a.oldName.startsWith(LOCAL_VAR_PREFIX) || (!externNames.contains(a.oldName) && prevNewName.startsWith(prefix))) { reservedNames.add(prevNewName); finalizeNameAssignment(a, prevNewName); } } } /** * Determines which new names to substitute for the original names. */ private void assignNames(Set<Assignment> varsToRename) { NameGenerator globalNameGenerator = new NameGenerator(reservedNames, prefix, reservedCharacters); // Local variables never need a prefix. NameGenerator localNameGenerator = prefix.isEmpty() ? globalNameGenerator : new NameGenerator(reservedNames, "", reservedCharacters); // Generated names and the assignments for non-local vars. List<

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> * The stack of basic blocks and scopes the current traversal is in. */ private final Deque<BasicBlock> blockStack = new ArrayDeque<BasicBlock>(); /** * Source of behavior at various points in the traversal. */ private final Behavior behavior; /** * Javascript compiler to use in traversing. */ private final AbstractCompiler compiler; /** * Only collect references for filtered variables. */ private final Predicate<Var> varFilter; /** * Constructor initializes block stack. */ ReferenceCollectingCallback(AbstractCompiler compiler, Behavior behavior) { this(compiler, behavior, Predicates.<Var>alwaysTrue()); } /** * Constructor only collects references that match the given variable. * * The test for Var equality uses reference equality, so it's necessary to * inject a scope when you traverse. */ ReferenceCollectingCallback(AbstractCompiler compiler, Behavior behavior, Predicate<Var> varFilter) { this.compiler = compiler; this.behavior = behavior; this.varFilter = varFilter; } /** * Convenience method for running this pass over a tree with this * class as a callback. */ public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); } /** * Gets the variables that were referenced in this callback. */ public Set<Var> getReferencedVariables() { return referenceMap.keySet(); } /** * Gets the reference collection for the given variable. */ public ReferenceCollection getReferenceCollection(Var v) { return referenceMap.get(v); } /** * For each node, update the block stack and reference collection * as appropriate. */ public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.NAME) { Var v = t.getScope().getVar(n.getString()); if (v != null && varFilter.apply(v)) { addReference(t, v, new Reference(n, parent, t, blockStack.peek())); } } if (isBlockBoundary(n, parent)) { blockStack.pop(); } } /** * Updates block stack and invokes any additional behavior. */ public void enterScope(

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> preciser scope to the next link. * If there is no next link, returns the blind scope. */ protected FlowScope nextPreciserScopeKnowingConditionOutcome(Node condition, FlowScope blindScope, boolean outcome) { return nextLink != null ? nextLink.getPreciserScopeKnowingConditionOutcome( condition, blindScope, outcome) : blindScope; } /** * Returns the type of a node in the given scope if the node corresponds to a * name whose type is capable of being refined. * @return The current type of the node if it can be refined, null otherwise. */ JSType getTypeIfRefinable(Node node, FlowScope scope) { switch (node.getType()) { case Token.NAME: StaticSlot<JSType> nameVar = scope.getSlot(node.getString()); if (nameVar != null) { JSType nameVarType = nameVar.getType(); if (nameVarType == null) { nameVarType = node.getJSType(); } return nameVarType; } return null; case Token.GETPROP: String qualifiedName = node.getQualifiedName(); if (qualifiedName == null) { return null; } StaticSlot<JSType> propVar = scope.getSlot(qualifiedName); JSType propVarType = null; if (propVar != null) { propVarType = propVar.getType(); } if (propVarType == null) { propVarType = node.getJSType(); } if (propVarType == null) { propVarType = getNativeType(UNKNOWN_TYPE); } return propVarType; } return null; } /** * Declares a refined type in {@code scope} for the name represented by * {@code node}. It must be possible to refine the type of the given node in * the given scope, as determined by {@link #getTypeIfRefinable}. */ protected void declareNameInScope(FlowScope scope, Node node, JSType type) { switch (node.getType()) { case Token.NAME: scope.inferSlotType(node.getString(), type); break; case Token.GETPROP: String qualifiedName

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>Scope(); final FunctionType functionType = (FunctionType) n.getJSType(); final String functionPrivateName = n.getFirstChild().getString(); if (functionPrivateName != null && functionPrivateName.length() > 0 && outerScope.isDeclared(functionPrivateName, false) && // Ideally, we would want to check whether the type in the scope // differs from the type being defined, but then the extern // redeclarations of built-in types generates spurious warnings. !(outerScope.getVar( functionPrivateName).getType() instanceof FunctionType)) { report(t, n, FUNCTION_MASKS_VARIABLE, functionPrivateName); } // TODO(user): Only traverse the function's body. The function's // name and arguments are traversed by the scope creator, and ideally // should not be traversed by the type checker. break; } return true; } /** * This is the meat of the type checking. It is basically one big switch, * with each case representing one type of parse tree node. The individual * cases are usually pretty straightforward. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. * @param parent The parent of the node n. */ public void visit(NodeTraversal t, Node n, Node parent) { JSType childType; JSType leftType, rightType; Node left, right; // To be explicitly set to false if the node is not typeable. boolean typeable = true; switch (n.getType()) { case Token.NAME: typeable = visitName(t, n, parent); break; case Token.LP: // If this is under a FUNCTION node, it is a parameter list and can be // ignored here. if (parent.getType() != Token.FUNCTION) { ensureTyped(t, n, getJSType(n.getFirstChild())); } else { typeable = false; } break; case Token.COMMA: ensureTyped(t, n, getJSType(n.getLastChild())); break; case Token.TRUE: case Token.FALSE

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>Traversal t, Node assign) { JSDocInfo info = assign.getJSDocInfo(); Node lvalue = assign.getFirstChild(); Node rvalue = assign.getLastChild(); if (lvalue.getType() == Token.GETPROP) { Node object = lvalue.getFirstChild(); JSType objectJsType = getJSType(object); String property = lvalue.getLastChild().getString(); // the first name in this getprop refers to an interface // we perform checks in addition to the ones below if (object.getType() == Token.GETPROP) { JSType jsType = getJSType(object.getFirstChild()); if (jsType.isInterface() && object.getLastChild().getString().equals("prototype")) { visitInterfaceGetprop(t, assign, object, property, lvalue, rvalue); } } // /** @type ... */object.name = ...; if (info != null && info.hasType()) { visitAnnotatedAssignGetprop(t, assign, info.getType().evaluate(t.getScope(), typeRegistry), object, property, rvalue); return; } // /** @enum ... */object.name = ...; if (info != null && info.hasEnumParameterType()) { checkEnumInitializer( t, rvalue, info.getEnumParameterType().evaluate( t.getScope(), typeRegistry)); return; } // object.prototype = ...; if (property.equals("prototype")) { if (objectJsType instanceof FunctionType) { FunctionType functionType = (FunctionType) objectJsType; if (functionType.isConstructor()) { JSType rvalueType = rvalue.getJSType(); validator.expectObject(t, rvalue, rvalueType, OVERRIDING_PROTOTYPE_WITH_NON_OBJECT); } } else { // TODO(user): might want to flag that } return; } // object.prototype.property = ...; if (object.getType() == Token.GETPROP) { Node object2 = object.getFirstChild(); String property2 = NodeUtil.getStringValue(object.getLastChild()); if ("prototype".equals(property2)) { JSType jsType = object2.getJSType();

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> if (jsType instanceof FunctionType) { FunctionType functionType = (FunctionType) jsType; if (functionType.isConstructor() || functionType.isInterface()) { checkDeclaredPropertyInheritance( t, assign, functionType, property, info, getJSType(rvalue)); } } else { // TODO(user): might want to flag that } return; } } // object.property = ...; ObjectType type = ObjectType.cast( objectJsType.restrictByNotNullOrUndefined()); if (type != null) { if (type.hasProperty(property) && !type.isPropertyTypeInferred(property) && !propertyIsImplicitCast(type, property)) { validator.expectCanAssignToPropertyOf( t, assign, getJSType(rvalue), type.getPropertyType(property), object, property); } return; } } else if (lvalue.getType() == Token.NAME) { // variable with inferred type case JSType rvalueType = getJSType(assign.getLastChild()); Var var = t.getScope().getVar(lvalue.getString()); if (var != null) { if (var.isTypeInferred()) { return; } } } // fall through case JSType leftType = getJSType(lvalue); Node rightChild = assign.getLastChild(); JSType rightType = getJSType(rightChild); if (validator.expectCanAssignTo( t, assign, rightType, leftType, "assignment")) { ensureTyped(t, assign, rightType); } else { ensureTyped(t, assign); } } /** * Returns true if any type in the chain has an implictCast annotation for * the given property. */ private boolean propertyIsImplicitCast(ObjectType type, String prop) { for (; type != null; type = type.getImplicitPrototype()) { JSDocInfo docInfo = type.getOwnPropertyJSDocInfo(prop); if (docInfo != null && docInfo.isImplicitCast()) { return true; } } return false; } /** * Given a constructor type and a property name, check that the property has * the JSDoc annotation @override

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>JSType(); if (type == null) { type = getNativeType(UNKNOWN_TYPE); Var var = t.getScope().getVar(n.getString()); if (var != null) { JSType varType = var.getType(); if (varType != null) { type = varType; } } } ensureTyped(t, n, type); return true; } /** * Visits a GETPROP node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. * @param parent The parent of <code>n</code> */ private void visitGetProp(NodeTraversal t, Node n, Node parent) { // GETPROP nodes have an assigned type on their node by the scope creator // if this is an enum declaration. The only namespaced enum declarations // that we allow are of the form object.name = ...; if (n.getJSType() != null && parent.getType() == Token.ASSIGN) { return; } // obj.prop or obj.method() // Lots of types can appear on the left, a call to a void function can // never be on the left. getPropertyType will decide what is acceptable // and what isn't. Node property = n.getLastChild(); Node objNode = n.getFirstChild(); JSType childType = getJSType(objNode); // TODO(user): remove in favor of flagging every property access on // non-object. if (!validator.expectNotVoid(t, n, childType, "undefined has no properties", getNativeType(OBJECT_TYPE))) { ensureTyped(t, n); return; } checkPropertyAccess(childType, property.getString(), t, n); ensureTyped(t, n); } /** * Make sure that the access of this property is ok. */ private void checkPropertyAccess(JSType childType, String propName, NodeTraversal t, Node n) { ObjectType objectType = childType.dereference(); if (objectType != null) { JSType propType = getJSType(n); if ((!

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> n.getLastChild(); validator.expectIndexMatch(t, n, getJSType(left), getJSType(right)); ensureTyped(t, n); } /** * Visits a VAR node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. */ private void visitVar(NodeTraversal t, Node n) { // TODO(nicksantos): Fix this so that the doc info always shows up // on the NAME node. We probably want to wait for the parser // merge to fix this. JSDocInfo varInfo = n.hasOneChild() ? n.getJSDocInfo() : null; for (Node name : n.children()) { Node value = name.getFirstChild(); // A null var would indicate a bug in the scope creation logic. Var var = t.getScope().getVar(name.getString()); if (value != null) { JSType valueType = getJSType(value); JSType nameType = var.getType(); nameType = (nameType == null) ? getNativeType(UNKNOWN_TYPE) : nameType; JSDocInfo info = name.getJSDocInfo(); if (info == null) { info = varInfo; } if (info != null && info.hasEnumParameterType()) { // var.getType() can never be null, this would indicate a bug in the // scope creation logic. checkEnumInitializer( t, value, info.getEnumParameterType().evaluate(t.getScope(), typeRegistry)); } else if (var.isTypeInferred()) { ensureTyped(t, name, valueType); } else { validator.expectCanAssignTo( t, value, valueType, nameType, "initializing variable"); } } } } /** * Visits a NEW node. */ private void visitNew(NodeTraversal t, Node n) { Node constructor = n.getFirstChild(); FunctionType type = getFunctionType(constructor); if (type != null && type.isConstructor()) { visitParameterList(t, n, type); ensureTyped(t,

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> n, type.getInstanceType()); } else { // TODO(user): add support for namespaced objects. if (constructor.getType() != Token.GETPROP) { // TODO(user): make the constructor node have lineno/charno // and use constructor for a more precise error indication. // It seems that GETPROP nodes are missing this information. Node line; if (constructor.getLineno() < 0 || constructor.getCharno() < 0) { line = n; } else { line = constructor; } report(t, line, NOT_A_CONSTRUCTOR); } ensureTyped(t, n); } } /** * Visits a {@link Token#FUNCTION} node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. */ private void visitFunction(NodeTraversal t, Node n) { JSDocInfo info = n.getJSDocInfo(); FunctionType functionType = (FunctionType) n.getJSType(); String functionPrivateName = n.getFirstChild().getString(); if (functionType.isInterface() || functionType.isConstructor()) { FunctionType baseConstructor = functionType. getPrototype().getImplicitPrototype().getConstructor(); if (baseConstructor != null && baseConstructor != getNativeType(OBJECT_FUNCTION_TYPE) && (baseConstructor.isConstructor() && functionType.isInterface() || baseConstructor.isInterface() && functionType.isConstructor())) { compiler.report( t.makeError(n, CONFLICTING_EXTENDED_TYPE, functionPrivateName)); } for (JSType baseInterface : functionType.getImplementedInterfaces()) { boolean badImplementedType = false; ObjectType baseInterfaceObj = ObjectType.cast(baseInterface); if (baseInterfaceObj != null) { FunctionType interfaceConstructor = baseInterfaceObj.getConstructor(); if (interfaceConstructor != null && !interfaceConstructor.isInterface()) { badImplementedType = true; } } else { badImplementedType = true; } if (badImplementedType) { report(t, n, BAD_IMPLEMENTED_TYPE

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>Typed(t, n, getNativeType(UNKNOWN_TYPE)); } private void ensureTyped(NodeTraversal t, Node n, JSTypeNative type) { ensureTyped(t, n, getNativeType(type)); } /** * Enforces type casts, and ensures the node is typed. * * A cast in the way that we use it in JSDoc annotations never * alters the generated code and therefore never can induce any runtime * operation. What this means is that a 'cast' is really just a compile * time constraint on the underlying value. In the future, we may add * support for run-time casts for compiled tests. * * To ensure some shred of sanity, we enforce the notion that the * type you are casting to may only meaningfully be a narrower type * than the underlying declared type. We also invalidate optimizations * on bad type casts. * * @param t The traversal object needed to report errors. * @param n The node getting a type assigned to it. * @param type The type to be assigned. */ private void ensureTyped(NodeTraversal t, Node n, JSType type) { // Make sure FUNCTION nodes always get function type. Preconditions.checkState(n.getType() != Token.FUNCTION || type instanceof FunctionType || type.isUnknownType()); JSDocInfo info = n.getJSDocInfo(); if (info != null) { if (info.hasType()) { JSType infoType = info.getType().evaluate(t.getScope(), typeRegistry); validator.expectCanCast(t, n, infoType, type); type = infoType; } if (info.isImplicitCast() && !inExterns) { String propName = n.getType() == Token.GETPROP ? n.getLastChild().getString() : "(missing)"; compiler.report( t.makeError(n, ILLEGAL_IMPLICIT_CAST, propName)); } } if (n.getJSType() == null) { n.setJSType(type); } } /** * Returns the percentage of nodes typed by the type checker. * @return a number between 0.0 and 100

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>/* * Copyright 2010 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.Node; /** * Look for references to the global RegExp object that would cause * regular expressions to be unoptimizable. * * @author johnlenz@google.com (John Lenz) */ class CheckRegExp extends AbstractPostOrderCallback implements CompilerPass { static final DiagnosticType REGEXP_REFERENCE = DiagnosticType.warning("JSC_REGEXP_REFERENCE", "References to the global RegExp object prevents " + "optimization of regular expressions."); private final AbstractCompiler compiler; private boolean globalRegExpPropertiesUsed = false; public boolean isGlobalRegExpPropertiesUsed() { return globalRegExpPropertiesUsed; } public CheckRegExp(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isReferenceName(n)) { String name = n.getString(); if (name.equals("RegExp") && t.getScope().getVar(name) == null) { int parentType = parent.getType(); boolean first = (n == parent.getFirstChild()); if (!((parentType == Token.NEW && first) || (parentType == Token.CALL && first) || (parentType == Token.INSTANCEOF && !

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>.javascript.jscomp.mozilla.rhino.ast.TryStatement; import com.google.javascript.jscomp.mozilla.rhino.ast.UnaryExpression; import com.google.javascript.jscomp.mozilla.rhino.ast.VariableDeclaration; import com.google.javascript.jscomp.mozilla.rhino.ast.VariableInitializer; import com.google.javascript.jscomp.mozilla.rhino.ast.WhileLoop; import com.google.javascript.jscomp.mozilla.rhino.ast.WithStatement; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.Collection; import java.util.Iterator; import java.util.Set; /** * IRFactory transforms the new AST to the old AST. * */ public class IRFactory { private final String sourceString; private final String sourceName; private final Config config; private final ErrorReporter errorReporter; private final TransformDispatcher transformDispatcher; // non-static for thread safety private final Set<String> ALLOWED_DIRECTIVES = Sets.newHashSet("use strict"); // Nodes with JSDoc comments, indexed by the text of the JSDoc comment. // // It's likely that two or more nodes in the same file may have the same // jsdoc comment. In general, that's ok. // // There's one edge case where this might cause problems. If two JSDoc // comments have the same text, and the first JSDoc comment is not attached // to a node, then the second node will get the first JSDoc comment // instead of the second. When this happens, it probably won't cause any // problems. The two JSDoc comments will be exactly the same, except for // their line numbers. Their line numbers will only be exposed for // type name resolution warnings. // // TODO(nicksantos): Change rhino to put the whole Comment object // on the Node. private final Multimap<String, NodeWithJsDoc> nodesWithJsDoc = LinkedHashMultimap.create(); // Use a template node for properties set on all nodes to minimize

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> the // memory footprint associated with these. private Node templateNode; // TODO(johnlenz): Consider creating a template pool for ORIGINALNAME_PROP. private IRFactory(String sourceString, String sourceName, Config config, ErrorReporter errorReporter) { this.sourceString = sourceString; this.sourceName = sourceName; this.config = config; this.errorReporter = errorReporter; this.transformDispatcher = new TransformDispatcher(); // The template node properties are applied to all nodes in this transform. this.templateNode = createTemplateNode(); } // Create a template node to use as a source of common attributes, this allows // the prop structure to be shared among all the node from this source file. // This reduces the cost of these properties to O(nodes) to O(files). private Node createTemplateNode() { // The Node type choice is arbitrary. Node templateNode = new Node(Token.SCRIPT); templateNode.putProp(Node.SOURCENAME_PROP, sourceName); return templateNode; } public static Node transformTree(AstRoot node, String sourceString, Config config, ErrorReporter errorReporter) { IRFactory irFactory = new IRFactory(sourceString, node.getSourceName(), config, errorReporter); Node irNode = irFactory.transform(node); // @license text gets appended onto the fileLevelJsDocBuilder as found, // and stored straight into the JSDocInfo for the root node. Node.FileLevelJsDocBuilder fileLevelJsDocBuilder = irNode.getJsDocBuilderForNode(); // fileOverviewInfo stores the last bit of fileoverview data we saw. // We only permit one, so throwing away extras is fair. // The fileOverviewInfo gets passed into parseJSDocInfo so that // it can detect when multiple @fileoverviews exist in the same file. JSDocInfo fileOverviewInfo = null; if (node.getComments() != null) { for (Comment comment : node.getComments()) { if (comment.getCommentType() == JSDOC) { JsDocInfoParser jsDocParser = irFactory.createJsDocInfoParser(comment.getValue(), comment.getLineno(), comment.getAbsolutePosition

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>() != -1) { irNode.setLineno(irNode.getFirstChild().getLineno()); irNode.setCharno(irNode.getFirstChild().getCharno()); } else { if (irNode.getLineno() == -1) { // If we didn't already set the line, then set it now. This avoids // cases like ParenthesizedExpression where we just return a previous // node, but don't want the new node to get its parent's line number. int lineno = node.getLineno(); irNode.setLineno(lineno); int charno = position2charno(node.getAbsolutePosition()); irNode.setCharno(charno); } } return irNode; } /** * Creates a JsDocInfoParser and parses the JsDoc string. * * Used both for handling individual JSDoc comments and for handling * file-level JSDoc comments (@fileoverview and @license). * * @param comment The JsDoc comment to parse. * @param lineno The line number of the node this comment is attached to. * @param fileLevelJsDocBuilder The builder for file-level JSDocInfo. * @param fileOverviewInfo The current @fileoverview JSDocInfo, so that the * parser may warn if another @fileoverview is found. May be null. * @return A JSDocInfoParser. Will contain either fileoverview jsdoc, or * normal jsdoc, or no jsdoc (if the method parses to the wrong level). */ private JsDocInfoParser createJsDocInfoParser( String comment, int lineno, int position, Node.FileLevelJsDocBuilder fileLevelJsDocBuilder, JSDocInfo fileOverviewInfo) { // The JsDocInfoParser expects the comment without the initial '/**'. int numOpeningChars = 3; JsDocInfoParser jsdocParser = new JsDocInfoParser( new JsDocTokenStream(comment.substring(numOpeningChars), lineno, position2charno(position) + numOpeningChars), sourceName, config, errorReporter); jsdocParser.setFileLevelJsDocBuilder(fileLevelJsDocBuilder); jsdocParser.setFileOverviewJSDoc

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>.STRING); } return ret; } @Override Node processArrayLiteral(ArrayLiteral literalNode) { if (literalNode.isDestructuring()) { reportDestructuringAssign(literalNode); } Node node = newNode(Token.ARRAYLIT); int skipCount = 0; for (AstNode child : literalNode.getElements()) { Node c = transform(child); if (c.getType() == Token.EMPTY) { skipCount++; } node.addChildToBack(c); } if (skipCount > 0) { int[] skipIndexes = new int[skipCount]; int i = 0; int j = 0; for (Node child : node.children()) { if (child.getType() == Token.EMPTY) { node.removeChild(child); skipIndexes[j] = i; j++; } i++; } node.putProp(Node.SKIP_INDEXES_PROP, skipIndexes); } return node; } @Override Node processAssignment(Assignment assignmentNode) { return processInfixExpression(assignmentNode); } @Override Node processAstRoot(AstRoot rootNode) { Node node = newNode(Token.SCRIPT); for (com.google.javascript.jscomp.mozilla.rhino.Node child : rootNode) { node.addChildToBack(transform((AstNode)child)); } parseDirectives(node); return node; } /** * Parse the directives, encode them in the AST, and remove their nodes. * * For information on ES5 directives, see section 14.1 of * Ecma-262, Edition 5. * * It would be nice if Rhino would eventually take care of this for * us, but right now their directive-processing is a one-off. */ private void parseDirectives(Node node) { // Remove all the directives, and encode them in the AST. Set<String> directives = null; while (isDirective(node.getFirstChild())) { String directive = node.removeFirstChild().getFirstChild().getString(); if (directives == null) { directives = Sets.newHashSet(directive); } else { directives.

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>add(directive); } } if (directives != null) { node.setDirectives(directives); } } private boolean isDirective(Node n) { if (n == null) return false; int nType = n.getType(); return (nType == Token.EXPR_RESULT || nType == Token.EXPR_VOID) && n.getFirstChild().getType() == Token.STRING && ALLOWED_DIRECTIVES.contains(n.getFirstChild().getString()); } @Override Node processBlock(Block blockNode) { return processGeneric(blockNode); } @Override Node processBreakStatement(BreakStatement statementNode) { Node node = newNode(Token.BREAK); if (statementNode.getBreakLabel() != null) { Node labelName = transform(statementNode.getBreakLabel()); // Change the NAME to LABEL_NAME labelName.setType(Token.LABEL_NAME); node.addChildToBack(labelName); } return node; } @Override Node processCatchClause(CatchClause clauseNode) { AstNode catchVar = clauseNode.getVarName(); Node node = newNode(Token.CATCH, transform(catchVar)); if (clauseNode.getCatchCondition() != null) { errorReporter.error( "Catch clauses are not supported", sourceName, clauseNode.getCatchCondition().getLineno(), "", 0); } node.addChildToBack(transformBlock(clauseNode.getBody())); return node; } @Override Node processConditionalExpression(ConditionalExpression exprNode) { return newNode( Token.HOOK, transform(exprNode.getTestExpression()), transform(exprNode.getTrueExpression()), transform(exprNode.getFalseExpression())); } @Override Node processContinueStatement(ContinueStatement statementNode) { Node node = newNode(Token.CONTINUE); if (statementNode.getLabel() != null) { Node labelName = transform(statementNode.getLabel()); // Change the NAME to LABEL_NAME labelName.setType(Token.LABEL_NAME); node.addChildToBack(labelName); } return node; } @Override Node processDoLoop(DoLoop loopNode) { return newNode( Token.DO, transformBlock

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> isUnhoistedNamedFunction = true; } } for (Reference reference : references) { if (reference == hoistedFn) { continue; } BasicBlock basicBlock = reference.getBasicBlock(); boolean isDeclaration = reference.isDeclaration(); if (isDeclaration) { // Look through all the declarations we've found so far, and // check if any of them are before this block. for (BasicBlock declaredBlock : blocksWithDeclarations) { if (declaredBlock.provablyExecutesBefore(basicBlock)) { compiler.report( JSError.make(reference.getSourceName(), reference.getNameNode(), checkLevel, REDECLARED_VARIABLE, v.name)); break; } } } if (isUnhoistedNamedFunction && !isDeclaration && isDeclaredInScope) { // Only allow an unhoisted named function to be used within the // block it is declared. for (BasicBlock declaredBlock : blocksWithDeclarations) { if (!declaredBlock.provablyExecutesBefore(basicBlock)) { compiler.report( JSError.make(reference.getSourceName(), reference.getNameNode(), AMBIGUOUS_FUNCTION_DECL, v.name)); break; } } } if (!isDeclaration && !isDeclaredInScope) { // Special case to deal with var goog = goog || {} Node grandparent = reference.getGrandparent(); if (grandparent.getType() == Token.NAME && grandparent.getString() == v.name) { continue; } // Only generate warnings if the scopes do not match in order // to deal with possible forward declarations and recursion if (reference.getScope() == v.scope) { compiler.report( JSError.make(reference.getSourceName(), reference.getNameNode(), checkLevel, UNDECLARED_REFERENCE, v.name)); } } if (isDeclaration) { blocksWithDeclarations.add(basicBlock); isDeclaredInScope = true; } } } } }

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>/* * Copyright 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; /** * Converts property accesses from quoted string syntax to dot syntax, where * possible. Dot syntax is more compact and avoids an object allocation in * IE 6. * */ class ConvertToDottedProperties extends AbstractPostOrderCallback implements CompilerPass { private final AbstractCompiler compiler; ConvertToDottedProperties(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.GETELEM: Node left = n.getFirstChild(); Node right = left.getNext(); if (right.getType() == Token.STRING && NodeUtil.isValidPropertyName(right.getString())) { n.removeChild(left); n.removeChild(right); parent.replaceChild(n, new Node(Token.GETPROP, left, right)); compiler.reportCodeChange(); } break; } } }

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>Compiler compiler, RedeclarationHandler redeclarationHandler) { this.compiler = compiler; this.redeclarationHandler = redeclarationHandler; } public Scope createScope(Node n, Scope parent) { sourceName = null; if (parent == null) { scope = new Scope(n, compiler); } else { scope = new Scope(parent, n); } scanRoot(n, parent); sourceName = null; Scope returnedScope = scope; scope = null; return returnedScope; } private void scanRoot(Node n, Scope parent) { if (n.getType() == Token.FUNCTION) { sourceName = (String) n.getProp(Node.SOURCENAME_PROP); final Node fnNameNode = n.getFirstChild(); final Node args = fnNameNode.getNext(); final Node body = args.getNext(); // Bleed the function name into the scope, if it hasn't // been declared in the outer scope. String fnName = fnNameNode.getString(); if (!fnName.isEmpty() && NodeUtil.isFunctionExpression(n)) { declareVar(fnName, fnNameNode, n, null, null, n); } // Args: Declare function variables Preconditions.checkState(args.getType() == Token.LP); for (Node a = args.getFirstChild(); a != null; a = a.getNext()) { Preconditions.checkState(a.getType() == Token.NAME); declareVar(a.getString(), a, args, n, null, n); } // Body scanVars(body, n); } else { // It's the global block Preconditions.checkState(scope.getParent() == null); scanVars(n, null); } } /** * Scans and gather variables declarations under a Node */ private void scanVars(Node n, Node parent) { switch (n.getType()) { case Token.VAR: // Declare all variables. e.g. var x = 1, y, z; for (Node child = n.getFirstChild(); child != null;) { Node next = child.getNext(); Preconditions.checkState(child.getType() == Token.NAME); String name = child.getString();

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> declareVar(name, child, n, parent, null, n); child = next; } return; case Token.FUNCTION: if (NodeUtil.isFunctionExpression(n)) { return; } String fnName = n.getFirstChild().getString(); if (fnName.isEmpty()) { // This is invalid, but allow it so the checks can catch it. return; } declareVar(fnName, n.getFirstChild(), n, parent, null, n); return; // should not examine function's children case Token.CATCH: Preconditions.checkState(n.getChildCount() == 2); Preconditions.checkState(n.getFirstChild().getType() == Token.NAME); // the first child is the catch var and the third child // is the code block final Node var = n.getFirstChild(); final Node block = var.getNext(); declareVar(var.getString(), var, n, parent, null, n); scanVars(block, n); return; // only one child to scan case Token.SCRIPT: sourceName = (String) n.getProp(Node.SOURCENAME_PROP); break; } // Variables can only occur in statement-level nodes, so // we only need to traverse children in a couple special cases. if (NodeUtil.isControlStructure(n) || NodeUtil.isStatementBlock(n)) { for (Node child = n.getFirstChild(); child != null;) { Node next = child.getNext(); scanVars(child, n); child = next; } } } /** * Interface for injectable duplicate handling. */ interface RedeclarationHandler { void onRedeclaration( Scope s, String name, Node n, Node parent, Node gramps, Node nodeWithLineNumber); } /** * The default handler for duplicate declarations. */ private class DefaultRedeclarationHandler implements RedeclarationHandler { public void onRedeclaration( Scope s, String name, Node n, Node parent, Node gramps, Node nodeWithLineNumber) { // Don't allow multiple variables to be declared at the top level scope if (scope.isGlobal()) { Scope.Var origVar = scope.getVar(name); Node origParent

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> } } super.add(n, context); } private String getTypeAnnotation(Node node) { JSType type = node.getJSType(); if (type instanceof FunctionType) { return getFunctionAnnotation(node); } else if (type != null && !type.isUnknownType() && !type.isEmptyType() && !type.isVoidType() && !type.isFunctionPrototypeType()) { return "/** @type {" + node.getJSType() + "} */\n"; } else { return ""; } } /** * @param fnNode A node for a function for which to generate a type annotation */ private String getFunctionAnnotation(Node fnNode) { Preconditions.checkState(fnNode.getType() == Token.FUNCTION); StringBuilder sb = new StringBuilder("/**\n"); JSType type = fnNode.getJSType(); if (type == null || type.isUnknownType()) { return ""; } FunctionType funType = (FunctionType) fnNode.getJSType(); // We need to use the child nodes of the function as the nodes for the // parameters of the function type do not have the real parameter names. // FUNCTION // NAME // LP // NAME param1 // NAME param2 if (fnNode != null) { Node paramNode = NodeUtil.getFnParameters(fnNode).getFirstChild(); // Param types for (Node n : funType.getParameters()) { // Bail out if the paramNode is not there. if (paramNode == null) { break; } sb.append(" * @param {" + getParameterNodeJSDocType(n) + "} "); sb.append(paramNode.getString()); sb.append("\n"); paramNode = paramNode.getNext(); } } // Return type JSType retType = funType.getReturnType(); if (retType != null && !retType.isUnknownType() && !retType.isEmptyType()) { sb.append(" * @return {" + retType + "}\n"); } // Constructor/interface if (funType.isConstructor() || funType.isInterface()) { FunctionType superConstructor = funType.getSuperClassConstructor(); if

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> { this(new ContextualRenamer()); } MakeDeclaredNamesUnique(Renamer renamer) { this.rootRenamer = renamer; } static CompilerPass getContextualRenameInverter(AbstractCompiler compiler) { return new ContextualRenameInverter(compiler); } @Override public void enterScope(NodeTraversal t) { Node declarationRoot = t.getScopeRoot(); Renamer renamer; if (nameStack.isEmpty()) { // If the contextual renamer is being used the starting context can not // be a function. Preconditions.checkState( declarationRoot.getType() != Token.FUNCTION || !(rootRenamer instanceof ContextualRenamer)); Preconditions.checkState(t.inGlobalScope()); renamer = rootRenamer; } else { renamer = nameStack.peek().forChildScope(); } if (declarationRoot.getType() == Token.FUNCTION) { // Add the function parameters Node fnParams = declarationRoot.getFirstChild().getNext(); for (Node c = fnParams.getFirstChild(); c != null; c = c.getNext()) { String name = c.getString(); renamer.addDeclaredName(name); } // Add the function body declarations Node functionBody = declarationRoot.getLastChild(); findDeclaredNames(functionBody, null, renamer); } else { // Add the block declarations findDeclaredNames(declarationRoot, null, renamer); } nameStack.push(renamer); } @Override public void exitScope(NodeTraversal t) { if (!t.inGlobalScope()) { nameStack.pop(); } } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.FUNCTION: { // Add recursive function name, if needed. // NOTE: "enterScope" is called after we need to pick up this name. Renamer renamer = nameStack.peek().forChildScope(); // If needed, add the function recursive name. String name = n.getFirstChild().getString(); if (name != null && !name.isEmpty() && parent != null && !NodeUtil.isFunctionDeclaration(n

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>)) { renamer.addDeclaredName(name); } nameStack.push(renamer); } break; case Token.CATCH: { Renamer renamer = nameStack.peek().forChildScope(); String name = n.getFirstChild().getString(); renamer.addDeclaredName(name); nameStack.push(renamer); } break; } return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.NAME: String newName = getReplacementName(n.getString()); if (newName != null) { Renamer renamer = nameStack.peek(); if (renamer.stripConstIfReplaced()) { // TODO(johnlenz): Do we need to do anything about the javadoc? n.removeProp(Node.IS_CONSTANT_NAME); } n.setString(newName); t.getCompiler().reportCodeChange(); } break; case Token.FUNCTION: // Remove function recursive name (if any). nameStack.pop(); break; case Token.CATCH: // Remove catch except name from the stack of names. nameStack.pop(); break; } } /** * Walks the stack of name maps and finds the replacement name for the * current scope. */ private String getReplacementName(String oldName) { for (Renamer names : nameStack) { String newName = names.getReplacementName(oldName); if (newName != null) { return newName; } } return null; } /** * Traverses the current scope and collects declared names. Does not * decent into functions or add CATCH exceptions. */ private void findDeclaredNames(Node n, Node parent, Renamer renamer) { // Do a shallow traversal, so don't traverse into function declarations, // except for the name of the function itself. if (parent == null || parent.getType() != Token.FUNCTION || n == parent.getFirstChild()) { if (NodeUtil.isVarDeclaration(n)) { renamer.addDeclaredName(n.getString()); } else if (Node

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>Util.isFunctionDeclaration(n)) { Node nameNode = n.getFirstChild(); renamer.addDeclaredName(nameNode.getString()); } for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { findDeclaredNames(c, n, renamer); } } } /** * Declared names renaming policy interface. */ interface Renamer { /** * Called when a declared name is found in the local current scope. */ void addDeclaredName(String name); /** * @return A replacement name, null if oldName is unknown or should not * be replaced. */ String getReplacementName(String oldName); /** * @return Whether the constant-ness of a name should be removed. */ boolean stripConstIfReplaced(); /** * @return A Renamer for a scope within the scope of the current Renamer. */ Renamer forChildScope(); } /** * Inverts the transformation by {@link ContextualRenamer}, when possible. */ static class ContextualRenameInverter implements ScopedCallback, CompilerPass { private final AbstractCompiler compiler; // The set of names referenced in the current scope. private Set<String> referencedNames = ImmutableSet.of(); // Stack reference sets. private Deque<Set<String>> referenceStack = new ArrayDeque<Set<String>>(); // Name are globally unique initially, so we don't need a per-scope map. private Map<String, List<Node>> nameMap = Maps.newHashMap(); private ContextualRenameInverter(AbstractCompiler compiler) { this.compiler = compiler; } public void process(Node externs, Node js) { NodeTraversal.traverse(compiler, js, this); } public static String getOrginalName(String name) { int index = indexOfSeparator(name); return (index == -1) ? name : name.substring(0, index); } private static int indexOfSeparator(String name) { return name.lastIndexOf(ContextualRenamer.UNIQUE_ID_SEPARATOR); } private boolean containsSeparator(String name) { return name.indexOf(ContextualRenamer.UNIQUE_ID_SEPARATOR) != -1; } /** *

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>isValidName(newName)) { newName = original + ContextualRenamer.UNIQUE_ID_SEPARATOR + String.valueOf(i++); } return newName; } /** * @return Whether the name is valid to use in the local scope. */ private boolean isValidName(String name) { if (TokenStream.isJSIdentifier(name) && !referencedNames.contains(name) && !name.equals(ARGUMENTS)) { return true; } return false; } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { return true; } @Override public void visit(NodeTraversal t, Node node, Node parent) { if (t.inGlobalScope()) { return; } if (NodeUtil.isReferenceName(node)) { String name = node.getString(); // Add all referenced names to the set so it is possible to check for // conflicts. referencedNames.add(name); // Store only references to candidate names in the node map. if (containsSeparator(name)) { addCandidateNameReference(name, node); } } } private void addCandidateNameReference(String name, Node n) { List<Node> nodes = nameMap.get(name); if (null == nodes) { nodes = Lists.newLinkedList(); nameMap.put(name, nodes); } nodes.add(n); } } /** * Rename every locally name to be unique, the first encountered declaration * (specifically global names) are left in their original form. Those that are * renamed are made unique by giving them a unique suffix based on * the number of declarations of the name. * * The root ContextualRenamer is assumed to be in GlobalScope. * * Used by the Normalize pass. * @see Normalize */ static class ContextualRenamer implements Renamer { private final Multiset<String> nameUsage; private final Map<String, String> declarations = Maps.newHashMap(); private final boolean global; final static String UNIQUE_ID_SEPARATOR = "$$"; ContextualRenamer() { this.global = true; nameUsage = HashMultiset.create(); } /** *

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> * possible. */ private void maybeCollapseIntoForStatements(Node n, Node parent) { // Only SCRIPT, BLOCK, and LABELs can have FORs that can be collapsed into. // LABELs are not supported here. if (parent == null || !NodeUtil.isStatementBlock(parent)) { return; } // Is the current node something that can be in a for loop initializer? if (!NodeUtil.isExpressionNode(n) && !NodeUtil.isVar(n)) { return; } // Is the next statement a valid FOR? Node nextSibling = n.getNext(); if (nextSibling == null) { return; } else if (NodeUtil.isForIn(nextSibling)) { Node forNode = nextSibling; Node forVar = forNode.getFirstChild(); if (NodeUtil.isName(forVar) && NodeUtil.isVar(n) && n.hasOneChild()) { Node name = n.getFirstChild(); if (!name.hasChildren() && forVar.getString().equals(name.getString())) { // Ok, the names match, and the var declaration does not have an // initializer. Move it into the loop. parent.removeChild(n); forNode.replaceChild(forVar, n); compiler.reportCodeChange(); } } } else if (nextSibling.getType() == Token.FOR && nextSibling.getFirstChild().getType() == Token.EMPTY) { // Does the current node contain an in operator? If so, embedding // the expression in a for loop can cause some Javascript parsers (such // as the Playstation 3's browser based on Access's NetFront // browser) to fail to parse the code. // See bug 1778863 for details. if (NodeUtil.containsType(n, Token.IN)) { return; } // Move the current node into the FOR loop initializer. Node forNode = nextSibling; Node oldInitializer = forNode.getFirstChild(); parent.removeChild(n); Node newInitializer; if (NodeUtil.isVar(n)) { newInitializer = n; } else { // Extract the expression from EXPR_RESULT node. Preconditions.checkState(n.

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>precation(NodeTraversal t, Node n, Node parent) { // Don't bother checking definitions or constructors. if (parent.getType() == Token.FUNCTION || parent.getType() == Token.VAR || parent.getType() == Token.NEW) { return; } Scope.Var var = t.getScope().getVar(n.getString()); JSDocInfo docInfo = var == null ? null : var.getJSDocInfo(); if (docInfo != null && docInfo.isDeprecated() && shouldEmitDeprecationWarning(t, n, parent)) { if (docInfo.getDeprecationReason() != null) { compiler.report( t.makeError(n, DEPRECATED_NAME_REASON, n.getString(), docInfo.getDeprecationReason())); } else { compiler.report( t.makeError(n, DEPRECATED_NAME, n.getString())); } } } /** * Checks the given GETPROP node to ensure that access restrictions are * obeyed. */ private void checkPropertyDeprecation(NodeTraversal t, Node n, Node parent) { // Don't bother checking constructors. if (parent.getType() == Token.NEW) { return; } ObjectType objectType = ObjectType.cast(dereference(n.getFirstChild().getJSType())); String propertyName = n.getLastChild().getString(); if (objectType != null) { String deprecationInfo = getPropertyDeprecationInfo(objectType, propertyName); if (deprecationInfo != null && shouldEmitDeprecationWarning(t, n, parent)) { if (!deprecationInfo.isEmpty()) { compiler.report( t.makeError(n, DEPRECATED_PROP_REASON, propertyName, validator.getReadableJSTypeName(n.getFirstChild(), true), deprecationInfo)); } else { compiler.report( t.makeError(n, DEPRECATED_PROP, propertyName, validator.getReadableJSTypeName(n.getFirstChild(), true))); } } } } /** * Determines whether the given name is visible in the current context. * @param t The current traversal. * @param name The name node. */ private void checkNameVisibility(NodeTraversal t, Node name, Node parent) {

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> Var var = t.getScope().getVar(name.getString()); if (var != null) { JSDocInfo docInfo = var.getJSDocInfo(); if (docInfo != null) { // If a name is private, make sure that we're in the same file. Visibility visibility = docInfo.getVisibility(); if (visibility == Visibility.PRIVATE && !t.getInput().getName().equals(docInfo.getSourceName())) { if (docInfo.isConstructor() && isValidPrivateConstructorAccess(parent)) { return; } compiler.report( t.makeError(name, BAD_PRIVATE_GLOBAL_ACCESS, name.getString(), docInfo.getSourceName())); } } } } /** * Determines whether the given property is visible in the current context. * @param t The current traversal. * @param getprop The getprop node. */ private void checkPropertyVisibility(NodeTraversal t, Node getprop, Node parent) { ObjectType objectType = ObjectType.cast(dereference(getprop.getFirstChild().getJSType())); String propertyName = getprop.getLastChild().getString(); if (objectType != null) { // Is this a normal property access, or are we trying to override // an existing property? boolean isOverride = t.inGlobalScope() && parent.getType() == Token.ASSIGN && parent.getFirstChild() == getprop; // Find the lowest property defined on a class with visibility // information. if (isOverride) { objectType = objectType.getImplicitPrototype(); } JSDocInfo docInfo = null; for (; objectType != null; objectType = objectType.getImplicitPrototype()) { docInfo = objectType.getOwnPropertyJSDocInfo(propertyName); if (docInfo != null && docInfo.getVisibility() != Visibility.INHERITED) { break; } } if (objectType == null) { // We couldn't find a visibility modifier; assume it's public. return; } boolean sameInput = t.getInput().getName().equals(docInfo.getSourceName()); Visibility visibility = docInfo.getVisibility(); JSType ownerType = normalizeClassType(objectType); if (isOverride)

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> public static String getMessage1(String messageId, Object arg1) { Object[] arguments = {arg1}; return getMessage(messageId, arguments); } public static String getMessage2( String messageId, Object arg1, Object arg2) { Object[] arguments = {arg1, arg2}; return getMessage(messageId, arguments); } public static String getMessage3( String messageId, Object arg1, Object arg2, Object arg3) { Object[] arguments = {arg1, arg2, arg3}; return getMessage(messageId, arguments); } public static String getMessage4( String messageId, Object arg1, Object arg2, Object arg3, Object arg4) { Object[] arguments = {arg1, arg2, arg3, arg4}; return getMessage(messageId, arguments); } /* OPT there's a noticable delay for the first error! Maybe it'd * make sense to use a ListResourceBundle instead of a properties * file to avoid (synchronized) text parsing. */ public static String getMessage(String messageId, Object[] arguments) { final String defaultResource = "rhino_ast.java.com.google.javascript.rhino.Messages"; Context cx = Context.getCurrentContext(); Locale locale = cx != null ? cx.getLocale() : Locale.getDefault(); // ResourceBundle does cacheing. ResourceBundle rb = ResourceBundle.getBundle(defaultResource, locale); String formatString; try { formatString = rb.getString(messageId); } catch (java.util.MissingResourceException mre) { throw new RuntimeException ("no message resource found for message property "+ messageId); } /* * It's OK to format the string, even if 'arguments' is null; * we need to format it anyway, to make double ''s collapse to * single 's. */ // TODO: MessageFormat is not available on pJava MessageFormat formatter = new MessageFormat(formatString); return formatter.format(arguments); } public static EcmaError constructError(String error, String message) { int[] linep = new int[1]; String filename = Context.getSourcePositionFromStack(linep); return constructError(error, message, filename, line

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> lineno, int charno) { errorReporter.warning(ScriptRuntime.getMessage0(messageId), sourceName, lineno, null, charno); } } // The DocInfo with the fileoverview tag for the whole file. private JSDocInfo fileOverviewJSDocInfo = null; private State state; private final Map<String, Annotation> annotationNames; private final Set<String> suppressionNames; private Node.FileLevelJsDocBuilder fileLevelJsDocBuilder; /** * Sets the JsDocBuilder for the file-level (root) node of this parse. The * parser uses the builder to append any preserve annotations it encounters * in jsdoc comments. * * @param fileLevelJsDocBuilder */ void setFileLevelJsDocBuilder( Node.FileLevelJsDocBuilder fileLevelJsDocBuilder) { this.fileLevelJsDocBuilder = fileLevelJsDocBuilder; } /** * Sets the file overview JSDocInfo, in order to warn about multiple uses of * the @fileoverview tag in a file. */ void setFileOverviewJSDocInfo(JSDocInfo fileOverviewJSDocInfo) { this.fileOverviewJSDocInfo = fileOverviewJSDocInfo; } private enum State { SEARCHING_ANNOTATION, SEARCHING_NEWLINE, NEXT_IS_ANNOTATION } JsDocInfoParser(JsDocTokenStream stream, String sourceName, Config config, ErrorReporter errorReporter) { this.stream = stream; this.sourceName = sourceName; this.jsdocBuilder = new JSDocInfoBuilder(config.parseJsDocDocumentation); this.annotationNames = config.annotationNames; this.suppressionNames = config.suppressionNames; this.errorReporter = errorReporter; } /** * Parses a string containing a JsDoc type declaration, returning the * type if the parsing succeeded or {@code null} if it failed. */ public static Node parseTypeString(String typeString) { Config config = new Config( Sets.<String>newHashSet(), Sets.<String>newHashSet(), false); JsDocInfoParser parser = new JsDocInfoParser( new JsDocTokenStream(typeString),

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> "typeparsing", config, NullErrorReporter.forNewRhino()); return parser.parseTopLevelTypeExpression(parser.next()); } /** * Parses a {@link JSDocInfo} object. This parsing method reads all tokens * returned by the {@link JsDocTokenStream#getJsDocToken()} method until the * {@link JsDocToken#EOC} is returned. * * @return {@code true} if JSDoc information was correctly parsed, * {@code false} otherwise */ boolean parse() { int lineno; int charno; // JSTypes are represented as Rhino AST nodes, and then resolved later. JSTypeExpression type; state = State.SEARCHING_ANNOTATION; skipEOLs(); JsDocToken token = next(); // Always record that we have a comment. if (jsdocBuilder.shouldParseDocumentation()) { ExtractionInfo blockInfo = extractBlockComment(token); token = blockInfo.token; if (!blockInfo.string.isEmpty()) { jsdocBuilder.recordBlockDescription(blockInfo.string); } } else { if (token != JsDocToken.ANNOTATION && token != JsDocToken.EOC) { // Mark that there was a description, but don't bother marking // what it was. jsdocBuilder.recordBlockDescription(""); } } // Parse the actual JsDoc. retry: for (;;) { switch (token) { case ANNOTATION: if (state == State.SEARCHING_ANNOTATION) { state = State.SEARCHING_NEWLINE; lineno = stream.getLineno(); charno = stream.getCharno(); String annotationName = stream.getString(); Annotation annotation = annotationNames.get(annotationName); if (annotation == null) { parser.addWarning("msg.bad.jsdoc.tag", annotationName, stream.getLineno(), stream.getCharno()); } else { // Mark the beginning of the annotation. jsdocBuilder.markAnnotation(annotationName, lineno, charno); switch (annotation) { case AUTHOR: if (jsdocBuilder.shouldParseDocumentation()) { ExtractionInfo authorInfo = extractSingleLineBlock(); String author =

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> = stream.getCharno(); type = null; if (token == JsDocToken.LC) { type = createJSTypeExpression( parseAndRecordTypeNode(token)); if (type == null) { // parsing error reported during recursive descent // recovering parsing token = eatTokensUntilEOL(); continue retry; } } // *Update* the token to that after the type annotation. token = current(); // Save the throw type. jsdocBuilder.recordThrowType(type); // Find the throw's description (if applicable). if (jsdocBuilder.shouldParseDocumentation()) { ExtractionInfo descriptionInfo = extractMultilineTextualBlock(token); String description = descriptionInfo.string; if (description.length() > 0) { jsdocBuilder.recordThrowDescription(type, description); } token = descriptionInfo.token; } else { token = eatTokensUntilEOL(token); } continue retry; case PARAM: skipEOLs(); token = next(); lineno = stream.getLineno(); charno = stream.getCharno(); type = null; if (token == JsDocToken.LC) { type = createJSTypeExpression( parseAndRecordParamTypeNode(token)); if (type == null) { // parsing error reported during recursive descent // recovering parsing token = eatTokensUntilEOL(); continue retry; } skipEOLs(); token = next(); lineno = stream.getLineno(); charno = stream.getCharno(); } String name = null; boolean isBracketedParam = JsDocToken.LB == token; if (isBracketedParam) { token = next(); } if (JsDocToken.STRING != token) { parser.addWarning("msg.missing.variable.name", lineno, charno); } else { name = stream.getString(); if (isBracketedParam) { token = next(); // Throw out JsDocToolkit's "default" parameter annotation. // It makes no sense under our type system. if (JsDocToken.EQUALS == token) { token = next(); if (JsDocToken.STRING == token) { token = next();

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> templateTypeName)) { parser.addWarning("msg.jsdoc.template.at.most.once", stream.getLineno(), stream.getCharno()); } token = templateInfo.token; continue retry; case VERSION: ExtractionInfo versionInfo = extractSingleLineBlock(); String version = versionInfo.string; if (version.length() == 0) { parser.addWarning("msg.jsdoc.versionmissing", stream.getLineno(), stream.getCharno()); } else { if (!jsdocBuilder.recordVersion(version)) { parser.addWarning("msg.jsdoc.extraversion", stream.getLineno(), stream.getCharno()); } } token = versionInfo.token; continue retry; case DEFINE: case RETURN: case THIS: case TYPE: case TYPEDEF: skipEOLs(); lineno = stream.getLineno(); charno = stream.getCharno(); token = next(); Node typeNode = parseAndRecordTypeNode(token, lineno, charno); if (annotation == Annotation.THIS) { typeNode = wrapNode(Token.BANG, typeNode); if (typeNode != null && token != JsDocToken.LC) { typeNode.putBooleanProp(Node.BRACELESS_TYPE, true); } } type = createJSTypeExpression(typeNode); if (type == null) { // error reported during recursive descent // recovering parsing } else { switch (annotation) { case DEFINE: if (!jsdocBuilder.recordDefineType(type)) { parser.addWarning("msg.jsdoc.define", lineno, charno); } break; case RETURN: if (!jsdocBuilder.recordReturnType(type)) { parser.addWarning( "msg.jsdoc.incompat.type", lineno, charno); break; } // *Update* the token to that after the type annotation. token = current(); // Find the return's description (if applicable). if (jsdocBuilder.shouldParseDocumentation()) { ExtractionInfo returnDescriptionInfo = extractMultilineTextualBlock(token); String returnDescription = returnDescriptionInfo.string; if (

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> = new HashSet<String>(); while (true) { if (match(JsDocToken.STRING)) { String name = stream.getString(); if (!suppressionNames.contains(name)) { parser.addWarning("msg.jsdoc.suppress.unknown", name, stream.getLineno(), stream.getCharno()); } suppressions.add(stream.getString()); token = next(); } else { parser.addWarning("msg.jsdoc.suppress", stream.getLineno(), stream.getCharno()); return token; } if (match(JsDocToken.PIPE)) { token = next(); } else { break; } } if (!match(JsDocToken.RC)) { parser.addWarning("msg.jsdoc.suppress", stream.getLineno(), stream.getCharno()); } else { token = next(); if (!jsdocBuilder.recordSuppressions(suppressions)) { parser.addWarning("msg.jsdoc.suppress.duplicate", stream.getLineno(), stream.getCharno()); } } } return token; } /** * Looks for a type expression at the current token and if found, * returns it. Note that this method consumes input. * * @param token The current token. * @return The type expression found or null if none. */ private Node parseAndRecordTypeNode(JsDocToken token) { return parseAndRecordTypeNode(token, token == JsDocToken.LC); } /** * Looks for a type expression at the current token and if found, * returns it. Note that this method consumes input. * * @param token The current token. * @param matchingLC Whether the type expression starts with a "{". * @return The type expression found or null if none. */ private Node parseAndRecordTypeNode(JsDocToken token, boolean matchingLC) { return parseAndRecordTypeNode(token, stream.getLineno(), stream.getCharno(), matchingLC, false); } /** * Looks for a type expression at the current token and if found, * returns it. Note that this method consumes input. * * @param token The current token.

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> The current token. * @param lineno The line of the type expression. * @param startCharno The starting character position of the type expression. * @param matchingLC Whether the type expression starts with a "{". * @param onlyParseSimpleNames If true, only simple type names are parsed * (via a call to parseTypeNameAnnotation instead of * parseTypeExpressionAnnotation). * @return The type expression found or null if none. */ private Node parseAndRecordTypeNode(JsDocToken token, int lineno, int startCharno, boolean matchingLC, boolean onlyParseSimpleNames) { Node typeNode = null; if (onlyParseSimpleNames) { typeNode = parseTypeNameAnnotation(token); } else { typeNode = parseTypeExpressionAnnotation(token); } if (typeNode != null && !matchingLC) { typeNode.putBooleanProp(Node.BRACELESS_TYPE, true); } int endCharno = stream.getCharno(); jsdocBuilder.markTypeNode(typeNode, lineno, startCharno, endCharno, matchingLC); return typeNode; } /** * Converts a JSDoc token to its string representation. */ private String toString(JsDocToken token) { switch (token) { case ANNOTATION: return "@" + stream.getString(); case BANG: return "!"; case COMMA: return ","; case COLON: return ":"; case GT: return ">"; case LB: return "["; case LC: return "{"; case LP: return "("; case LT: return ".<"; case QMARK: return "?"; case PIPE: return "|"; case RB: return "]"; case RC: return "}"; case RP: return ")"; case STAR: return "*"; case ELLIPSIS: return "..."; case EQUALS: return "="; case STRING: return stream.getString(); default: throw new IllegalStateException(token.toString()); } } /** * Constructs a new {@code JSTypeExpression}. * @param n A node. May be null. */ private JSTypeExpression createJSTypeExpression(Node n) {

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> return wrapNode(Token.BANG, basicTypeExpr); } } return basicTypeExpr; } } /** * BasicTypeExpression := '*' | 'null' | 'undefined' | TypeName * | FunctionType | UnionType | RecordType | ArrayType */ private Node parseBasicTypeExpression(JsDocToken token) { if (token == JsDocToken.STAR) { return newNode(Token.STAR); } else if (token == JsDocToken.LB) { skipEOLs(); return parseArrayType(next()); } else if (token == JsDocToken.LC) { skipEOLs(); return parseRecordType(next()); } else if (token == JsDocToken.LP) { skipEOLs(); return parseUnionType(next()); } else if (token == JsDocToken.STRING) { String string = stream.getString(); if ("function".equals(string)) { skipEOLs(); return parseFunctionType(next()); } else if ("null".equals(string) || "undefined".equals(string)) { return newStringNode(string); } else { return parseTypeName(token); } } return reportGenericTypeSyntaxWarning(); } /** * TypeName := NameExpression | NameExpression TypeApplication * TypeApplication := '.<' TypeExpressionList '>' * TypeExpressionList := TypeExpression // a white lie */ private Node parseTypeName(JsDocToken token) { if (token != JsDocToken.STRING) { return reportGenericTypeSyntaxWarning(); } Node typeName = newStringNode(stream.getString()); if (match(JsDocToken.LT)) { next(); skipEOLs(); Node memberType = parseTypeExpressionList(next()); if (memberType != null) { typeName.addChildToFront(memberType); skipEOLs(); if (!match(JsDocToken.GT)) { return reportTypeSyntaxWarning("msg.jsdoc.missing.gt"); } next(); } } return typeName; } /** * FunctionType := 'function' FunctionSignatureType * FunctionSignatureType := * TypeParameters '(' 'this' ':' TypeName, ParametersType ')' ResultType */ private Node parseFunctionType(JsDocToken token)

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> { // NOTE(nicksantos): We're not implementing generics at the moment, so // just throw out TypeParameters. if (token != JsDocToken.LP) { return reportTypeSyntaxWarning("msg.jsdoc.missing.lp"); } Node functionType = newNode(Token.FUNCTION); Node parameters = null; skipEOLs(); if (!match(JsDocToken.RP)) { token = next(); boolean hasParams = true; if (token == JsDocToken.STRING && "this".equals(stream.getString())) { if (match(JsDocToken.COLON)) { next(); skipEOLs(); Node thisType = wrapNode(Token.THIS, parseTypeName(next())); if (thisType == null) { return null; } functionType.addChildToFront(thisType); } else { return reportTypeSyntaxWarning("msg.jsdoc.missing.colon"); } if (match(JsDocToken.COMMA)) { next(); skipEOLs(); token = next(); } else { hasParams = false; } } if (hasParams) { parameters = parseParametersType(token); if (parameters == null) { return null; } } } if (parameters != null) { functionType.addChildToBack(parameters); } skipEOLs(); if (!match(JsDocToken.RP)) { return reportTypeSyntaxWarning("msg.jsdoc.missing.rp"); } skipEOLs(); Node resultType = parseResultType(next()); if (resultType == null) { return null; } else { functionType.addChildToBack(resultType); } return functionType; } /** * ParametersType := RestParameterType | NonRestParametersType * | NonRestParametersType ',' RestParameterType * RestParameterType := '...' Identifier * NonRestParametersType := ParameterType ',' NonRestParametersType * | ParameterType * | OptionalParametersType * OptionalParametersType := OptionalParameterType * | OptionalParameterType, OptionalParametersType * OptionalParameterType := ParameterType= * ParameterType := TypeExpression | Identifier ':' TypeExpression */ // NOTE(nicksantos): The official ES4 grammar forces optional

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> break; } } while (match(JsDocToken.COMMA)); } if (isVarArgs && match(JsDocToken.COMMA)) { return reportTypeSyntaxWarning("msg.jsdoc.function.varargs"); } // The right paren will be checked by parseFunctionType return paramsType; } /** * ResultType := <empty> | ':' void | ':' TypeExpression */ private Node parseResultType(JsDocToken token) { skipEOLs(); if (!match(JsDocToken.COLON)) { return newNode(Token.EMPTY); } token = next(); skipEOLs(); if (match(JsDocToken.STRING) && "void".equals(stream.getString())) { next(); return newNode(Token.VOID); } else { return parseTypeExpression(next()); } } /** * UnionType := '(' TypeUnionList ')' * TypeUnionList := TypeExpression | TypeExpression '|' TypeUnionList * * We've removed the empty union type. */ private Node parseUnionType(JsDocToken token) { return parseUnionTypeWithAlternate(token, null); } /** * Create a new union type, with an alternate that has already been * parsed. The alternate may be null. */ private Node parseUnionTypeWithAlternate(JsDocToken token, Node alternate) { Node union = newNode(Token.PIPE); if (alternate != null) { union.addChildToBack(alternate); } Node expr = null; do { if (expr != null) { skipEOLs(); token = next(); Preconditions.checkState( token == JsDocToken.PIPE || token == JsDocToken.COMMA); boolean isPipe = token == JsDocToken.PIPE; if (isPipe && match(JsDocToken.PIPE)) { // We support double pipes for backwards compatiblity. next(); } skipEOLs(); token = next(); } expr = parseTypeExpression(token); if (expr == null) { return null; } union.addChildToBack(expr); // We support commas for backwards compatiblity. } while (match(JsDocToken.PIPE, JsDocToken.COMMA)); if

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>(JsDocToken.COMMA)) { break; } // Move to the comma token. next(); // Move to the token passed the comma. skipEOLs(); token = next(); } while (true); return fieldTypeList; } /** * FieldType := FieldName | FieldName ':' TypeExpression */ private Node parseFieldType(JsDocToken token) { Node fieldName = parseFieldName(token); if (fieldName == null) { return null; } skipEOLs(); if (!match(JsDocToken.COLON)) { return fieldName; } // Move to the colon. next(); // Move to the token after the colon and parse // the type expression. skipEOLs(); Node typeExpression = parseTypeExpression(next()); if (typeExpression == null) { return null; } Node fieldType = newNode(Token.COLON); fieldType.addChildToBack(fieldName); fieldType.addChildToBack(typeExpression); return fieldType; } /** * FieldName := NameExpression | StringLiteral | NumberLiteral | * ReservedIdentifier */ private Node parseFieldName(JsDocToken token) { switch (token) { case STRING: String string = stream.getString(); return newStringNode(string); default: return null; } } private Node wrapNode(int type, Node n) { return n == null ? null : new Node(type, n, stream.getLineno(), stream.getCharno()); } private Node newNode(int type) { return new Node(type, stream.getLineno(), stream.getCharno()); } private Node newStringNode(String s) { return Node.newString(s, stream.getLineno(), stream.getCharno()); } private Node reportTypeSyntaxWarning(String warning) { parser.addWarning(warning, stream.getLineno(), stream.getCharno()); return null; } private Node reportGenericTypeSyntaxWarning() { return reportTypeSyntaxWarning("msg.jsdoc.type.syntax"); } /** * Eats tokens until {@link JsDocToken#EOL} included, and switches back the * state to {@link State#SEARCHING_ANNOTATION}. */

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>Graph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) { liveness.markAllParametersEscaped(); } liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { if (t.inGlobalScope()) { return; } colorings.pop(); } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (colorings.isEmpty() || !NodeUtil.isName(n) || NodeUtil.isFunction(parent)) { // Don't rename named functions. return; } Var var = t.getScope().getVar(n.getString()); GraphNode<Var, ?> vNode = colorings.peek().getGraph().getNode(var); if (vNode == null) { // This is not a local. return; } Var coalescedVar = colorings.peek().getPartitionSuperNode(var); if (!usePseudoNames) { if (vNode.getValue().equals(coalescedVar)) { // The coalesced name is itself, nothing to do. return; } // Rename. n.setString(coalescedVar.name); compiler.reportCodeChange(); if (NodeUtil.isVar(parent)) { removeVarDeclaration(n); } } else { // This

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> isReadFrom(use, n))) { crossed = true; } } private static boolean isAssignTo(Var var, Node n, Node parent) { if (NodeUtil.isName(n) && var.getName().equals(n.getString()) && parent != null) { if (parent.getType() == Token.LP) { // In a function declaration, the formal parameters are assigned. return true; } else if (NodeUtil.isVar(parent)) { // If this is a VAR declaration, if the name node has a child, we are // assigning to that name. return n.hasChildren(); } return false; // Definitely a read. } else { // Lastly, any assignmentOP is also an assign. Node name = n.getFirstChild(); return name != null && NodeUtil.isName(name) && var.getName().equals(name.getString()) && NodeUtil.isAssignmentOp(n); } } private static boolean isReadFrom(Var var, Node name) { return name != null && NodeUtil.isName(name) && var.getName().equals(name.getString()) && !NodeUtil.isLhs(name, name.getParent()); } } }

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> n) { isSet = true; type = getValueType(n.getNext()); } break; case Token.GETPROP: return; case Token.FUNCTION: Node gramps = parent.getParent(); if (gramps == null || NodeUtil.isFunctionExpression(parent)) return; isSet = true; type = Name.Type.FUNCTION; break; } } name = n.getString(); break; case Token.GETPROP: // This may be a namespaced name get or set. if (parent != null) { switch (parent.getType()) { case Token.ASSIGN: if (parent.getFirstChild() == n) { isSet = true; type = getValueType(n.getNext()); isPropAssign = true; } break; case Token.GETPROP: return; } } name = n.getQualifiedName(); if (name == null) return; break; default: return; } // We are only interested in global names. Scope scope = t.getScope(); if (!isGlobalNameReference(name, scope)) { return; } if (isSet) { if (isGlobalScope(scope)) { handleSetFromGlobal(t, n, parent, name, isPropAssign, type); } else { handleSetFromLocal(t, n, parent, name); } } else { handleGet(t, n, parent, name); } } /** * Gets the fully qualified name corresponding to an object literal key, * as long as it and its prefix property names are valid JavaScript * identifiers. The object literal may be nested inside of other object * literals. * * For example, if called with node {@code n} representing "z" in any of * the following expressions, the result would be "w.x.y.z": * <code> var w = {x: {y: {z: 0}}}; </code> * <code> w.x = {y: {z: 0}}; </code> * <code> w.x.y = {'a': 0, 'z': 0}; </code> * * @param n A child of an OBJL

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>IT node * @return The global name, or null if {@code n} doesn't correspond to the * key of an object literal that can be named */ String getNameForObjLitKey(Node n) { // Verify that this node is a key in the object literal (odd numbered). Node parent = n.getParent(); for (Node walker = parent.getFirstChild(); walker != n; walker = walker.getNext().getNext()) { if (walker == null) { return null; } } Node gramps = parent.getParent(); if (gramps == null) { return null; } String name; switch (gramps.getType()) { case Token.NAME: // VAR // NAME (gramps) // OBJLIT (parent) // STRING (n) Node greatGramps = gramps.getParent(); if (greatGramps == null || greatGramps.getType() != Token.VAR) { return null; } name = gramps.getString(); break; case Token.ASSIGN: // ASSIGN (gramps) // NAME|GETPROP // OBJLIT (parent) // STRING (n) Node lvalue = gramps.getFirstChild(); name = lvalue.getQualifiedName(); break; case Token.OBJECTLIT: // OBJLIT (gramps) // STRING // OBJLIT (parent) // STRING (n) Node key = gramps.getChildBefore(parent); if (key.getType() == Token.STRING) { name = getNameForObjLitKey(key); } else { return null; } break; default: return null; } if (name != null) { String key = n.getString(); if (TokenStream.isJSIdentifier(key)) { return name + '.' + key; } } return null; } /** * Gets the type of a value or simple expression. * * @param n An rvalue in an assignment or variable declaration (not null) * @return A {@link Name.Type} */ Name.Type getValueType(Node n

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>g. var a = a ? a : {}). type = determineGetTypeForHookOrBooleanExpr(t, parent, name); } break; default: type = Ref.Type.ALIASING_GET; break; } } handleGet(t, n, parent, name, type); } /** * Determines whether the result of a hook (x?y:z) or boolean expression * (x||y) or (x&&y) is assigned to a specific global name. * * @param t The traversal * @param parent The parent of the current node in the traversal. This node * should already be known to be a HOOK, AND, or OR node. * @param name A name that is already known to be global in the current * scope (e.g. "a" or "a.b.c.d") * @return The expression's get type, either {@link Ref.Type#DIRECT_GET} or * {@link Ref.Type#ALIASING_GET} */ Ref.Type determineGetTypeForHookOrBooleanExpr( NodeTraversal t, Node parent, String name) { Node prev = parent; for (Node anc : parent.getAncestors()) { switch (anc.getType()) { case Token.EXPR_RESULT: case Token.VAR: case Token.IF: case Token.WHILE: case Token.FOR: case Token.TYPEOF: case Token.VOID: case Token.NOT: case Token.BITNOT: case Token.POS: case Token.NEG: return Ref.Type.DIRECT_GET; case Token.HOOK: if (anc.getFirstChild() == prev) { return Ref.Type.DIRECT_GET; } break; case Token.ASSIGN: if (!name.equals(anc.getFirstChild().getQualifiedName())) { return Ref.Type.ALIASING_GET; } break; case Token.NAME: // a variable declaration if (!name.equals(anc.getString())) { return Ref.Type.ALIASING_GET; } break; case Token.CALL: if (anc.getFirstChild() != prev) { return Ref.Type.ALIASING_GET; } break

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> public void exitScope(NodeTraversal t) { if (t.getScopeDepth() == 2) { aliases.clear(); } } @Override public final boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION && t.inGlobalScope()) { // Do not traverse in to functions except for goog.scope functions. if (parent == null || !isCallToScopeMethod(parent)) { return false; } } return true; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (isCallToScopeMethod(n)) { if (!NodeUtil.isExpressionNode(parent)) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first // child is the "goog.scope" and the second should be the parameter. report(t, n, GOOG_SCOPE_HAS_BAD_PARAMETERS); } else { Node anonymousFnNode = n.getChildAtIndex(1); if (!NodeUtil.isFunction(anonymousFnNode) || NodeUtil.getFunctionName(anonymousFnNode) != null || NodeUtil.getFnParameters(anonymousFnNode).hasChildren()) { report(t, anonymousFnNode, GOOG_SCOPE_HAS_BAD_PARAMETERS); } else { scopeCalls.add(n); } } } if (t.getScopeDepth() == 2) { int type = n.getType(); if (type == Token.NAME && parent.getType() == Token.VAR) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { aliases.put(n.getString(), t.getScope().getVar(n.getString())); aliasDefinitions.add(n); // If we found an alias, we are done. return; } else { // TODO(robbyw):

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); } } if (type == Token.NAME && NodeUtil.isAssignmentOp(parent) && n == parent.getFirstChild()) { report(t, n, GOOG_SCOPE_ALIAS_REDEFINED, n.getString()); } if (type == Token.RETURN) { report(t, n, GOOG_SCOPE_USES_RETURN); } else if (type == Token.THIS) { report(t, n, GOOG_SCOPE_REFERENCES_THIS); } else if (type == Token.THROW) { report(t, n, GOOG_SCOPE_USES_THROW); } } if (t.getScopeDepth() >= 2) { if (n.getType() == Token.NAME) { String name = n.getString(); Var aliasVar = aliases.get(name); // Check if this name points to an alias. if (aliasVar != null && t.getScope().getVar(name) == aliasVar) { // Note, to support the transitive case, it's important we don't // clone aliasedNode here. For example, // var g = goog; var d = g.dom; d.createElement('DIV'); // The node in aliasedNode (which is "g") will be replaced in the // changes pass above with "goog". If we cloned here, we'd end up // with <code>g.dom.createElement('DIV')</code>. Node aliasedNode = aliasVar.getInitialValue(); aliasUsages.add(new AliasedNode(n, aliasedNode)); } } JSDocInfo info = n.getJSDocInfo(); if (info != null) { for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.getType() == Token.STRING) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> value of a node that represents a expression. This method * effectively emulates the <code>Boolean()</code> JavaScript cast function. * Note: unlike getBooleanValue this function does not return UNKNOWN * for expressions with side-effects. */ static TernaryValue getExpressionBooleanValue(Node n) { switch (n.getType()) { case Token.ASSIGN: case Token.COMMA: // For ASSIGN and COMMA the value is the value of the RHS. return getExpressionBooleanValue(n.getLastChild()); case Token.NOT: TernaryValue value = getExpressionBooleanValue(n.getLastChild()); return value.not(); case Token.AND: { TernaryValue lhs = getExpressionBooleanValue(n.getFirstChild()); TernaryValue rhs = getExpressionBooleanValue(n.getLastChild()); return lhs.and(rhs); } case Token.OR: { TernaryValue lhs = getExpressionBooleanValue(n.getFirstChild()); TernaryValue rhs = getExpressionBooleanValue(n.getLastChild()); return lhs.or(rhs); } case Token.HOOK: { TernaryValue trueValue = getExpressionBooleanValue( n.getFirstChild().getNext()); TernaryValue falseValue = getExpressionBooleanValue(n.getLastChild()); if (trueValue.equals(falseValue)) { return trueValue; } else { return TernaryValue.UNKNOWN; } } default: return getBooleanValue(n); } } /** * Gets the boolean value of a node that represents a literal. This method * effectively emulates the <code>Boolean()</code> JavaScript cast function. */ static TernaryValue getBooleanValue(Node n) { switch (n.getType()) { case Token.STRING: return TernaryValue.forBoolean(n.getString().length() > 0); case Token.NUMBER: return TernaryValue.forBoolean(n.getDouble() != 0); case Token.NULL: case Token.FALSE: case Token.VOID: return TernaryValue.FALSE; case Token.NAME: String name = n.getString(); if ("undefined".equals(name) || "NaN".equals(name)) { // We assume here that programs

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> don't change the value of the keyword // undefined to something other than the value undefined. return TernaryValue.FALSE; } else if ("Infinity".equals(name)) { return TernaryValue.TRUE; } break; case Token.TRUE: case Token.ARRAYLIT: case Token.OBJECTLIT: case Token.REGEXP: return TernaryValue.TRUE; } return TernaryValue.UNKNOWN; } /** * Gets the value of a node as a String, or null if it cannot be converted. * When it returns a non-null String, this method effectively emulates the * <code>String()</code> JavaScript cast function. */ static String getStringValue(Node n) { // TODO(user): Convert constant array, object, and regex literals as well. switch (n.getType()) { case Token.NAME: case Token.STRING: return n.getString(); case Token.NUMBER: double value = n.getDouble(); long longValue = (long) value; // Return "1" instead of "1.0" if (longValue == value) { return Long.toString(longValue); } else { return Double.toString(n.getDouble()); } case Token.FALSE: case Token.TRUE: case Token.NULL: return Node.tokenToName(n.getType()); case Token.VOID: return "undefined"; } return null; } /** * Gets the function's name. This method recognizes five forms: * <ul> * <li>{@code function name() ...}</li> * <li>{@code var name = function() ...}</li> * <li>{@code qualified.name = function() ...}</li> * <li>{@code var name2 = function name1() ...}</li> * <li>{@code qualified.name2 = function name1() ...}</li> * </ul> * In two last cases with named function expressions, the second name is * returned (the variable of qualified name). * * @param n a node whose type is {@link Token#FUNCTION} * @return the function's name, or {@code null} if it has no name */ static String getFunctionName(Node n) { Node

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> parent = n.getParent(); String name = n.getFirstChild().getString(); switch (parent.getType()) { case Token.NAME: // var name = function() ... // var name2 = function name1() ... return parent.getString(); case Token.ASSIGN: // qualified.name = function() ... // qualified.name2 = function name1() ... return parent.getFirstChild().getQualifiedName(); default: // function name() ... return name != null && name.length() != 0 ? name : null; } } /** * Returns true if this is an immutable value. */ static boolean isImmutableValue(Node n) { switch (n.getType()) { case Token.STRING: case Token.NUMBER: case Token.NULL: case Token.TRUE: case Token.FALSE: return true; case Token.VOID: case Token.NEG: return isImmutableValue(n.getFirstChild()); case Token.NAME: String name = n.getString(); // We assume here that programs don't change the value of the keyword // undefined to something other than the value undefined. return "undefined".equals(name) || "Infinity".equals(name) || "NaN".equals(name); } return false; } /** * Returns true if this is a literal value. We define a literal value * as any node that evaluates to the same thing regardless of when or * where it is evaluated. So /xyz/ and [3, 5] are literals, but * the name a is not. * * Function literals do not meet this definition, because they * lexically capture variables. For example, if you have * <code> * function() { return a; } * </code> * If it is evaluated in a different scope, then it * captures a different variable. Even if the function did not read * any captured vairables directly, it would still fail this definition, * because it affects the lifecycle of variables in the enclosing scope. * * However, a function literal with respect to a particular scope is * a literal. * * @param includeFunctions If true, all function expressions will be * treated as literals. */ static boolean isLiteralValue

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> } return true; default: if (isSimpleOperatorType(n.getType())) { break; } if (isAssignmentOp(n)) { // Assignments will have side effects if // a) The RHS has side effects, or // b) The LHS has side effects, or // c) A name on the LHS will exist beyond the life of this statement. if (checkForStateChangeHelper( n.getFirstChild(), checkForNewObjects, compiler) || checkForStateChangeHelper( n.getLastChild(), checkForNewObjects, compiler)) { return true; } Node current = n.getFirstChild(); for (; current.getType() == Token.GETPROP || current.getType() == Token.GETELEM; current = current.getFirstChild()) { } return !isLiteralValue(current, true); } return true; } for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (checkForStateChangeHelper(c, checkForNewObjects, compiler)) { return true; } } return false; } /** * Do calls to this constructor have side effects? * * @param callNode - construtor call node */ static boolean constructorCallHasSideEffects(Node callNode) { return constructorCallHasSideEffects(callNode, null); } static boolean constructorCallHasSideEffects( Node callNode, AbstractCompiler compiler) { Preconditions.checkArgument( callNode.getType() == Token.NEW, "Expected NEW node, got " + Token.name(callNode.getType())); if (callNode.isNoSideEffectsCall()) { return false; } Node nameNode = callNode.getFirstChild(); if (nameNode.getType() == Token.NAME && CONSTRUCTORS_WITHOUT_SIDE_EFFECTS.contains(nameNode.getString())) { return false; } return true; } // A list of built-in object creation or primitive type cast functions that // can also be called as constructors but lack side-effects. // TODO(johnlenz): consider adding an extern annotation for this. private static final Set<String> BUILTIN_FUNCTIONS_WITHOUT_SIDEEFFECTS =

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> ImmutableSet.of( "Object", "Array", "String", "Number", "Boolean", "RegExp", "Error"); private static final Set<String> REGEXP_METHODS = ImmutableSet.of("test", "exec"); private static final Set<String> STRING_REGEXP_METHODS = ImmutableSet.of("match", "replace", "search", "split"); /** * Returns true if calls to this function have side effects. * * @param callNode - function call node */ static boolean functionCallHasSideEffects( Node callNode) { return functionCallHasSideEffects(callNode, null); } /** * Returns true if calls to this function have side effects. * * @param callNode The call node to inspected. * @param compiler A compiler object to provide program state changing * context information. Can be null. */ static boolean functionCallHasSideEffects( Node callNode, @Nullable AbstractCompiler compiler) { Preconditions.checkArgument( callNode.getType() == Token.CALL, "Expected CALL node, got " + Token.name(callNode.getType())); if (callNode.isNoSideEffectsCall()) { return false; } Node nameNode = callNode.getFirstChild(); // Built-in functions with no side effects. if (nameNode.getType() == Token.NAME) { String name = nameNode.getString(); if (BUILTIN_FUNCTIONS_WITHOUT_SIDEEFFECTS.contains(name)) { return false; } } else if (nameNode.getType() == Token.GETPROP) { // Functions in the "Math" namespace have no side effects. if (nameNode.getFirstChild().getType() == Token.NAME) { String namespaceName = nameNode.getFirstChild().getString(); if (namespaceName.equals("Math")) { return false; } } if (compiler != null && !compiler.hasRegExpGlobalReferences()) { if (nameNode.getFirstChild().getType() == Token.REGEXP && REGEXP_METHODS.contains(nameNode.getLastChild().getString())) { return false; } else if (nameNode.getFirstChild().getType() == Token.STRING && STRING_REGEXP_METHODS.

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>contains( nameNode.getLastChild().getString())) { Node param = nameNode.getNext(); if (param != null && (param.getType() == Token.STRING || param.getType() == Token.REGEXP)) return false; } } } return true; } /** * Returns true if the current node's type implies side effects. * * This is a non-recursive version of the may have side effects * check; used to check wherever the current node's type is one of * the reason's why a subtree has side effects. */ static boolean nodeTypeMayHaveSideEffects(Node n) { return nodeTypeMayHaveSideEffects(n, null); } static boolean nodeTypeMayHaveSideEffects(Node n, AbstractCompiler compiler) { if (isAssignmentOp(n)) { return true; } switch(n.getType()) { case Token.DELPROP: case Token.DEC: case Token.INC: case Token.THROW: return true; case Token.CALL: return NodeUtil.functionCallHasSideEffects(n, compiler); case Token.NEW: return NodeUtil.constructorCallHasSideEffects(n, compiler); case Token.NAME: // A variable definition. return n.hasChildren(); default: return false; } } /** * @return Whether the tree can be affected by side-effects or * has side-effects. */ static boolean canBeSideEffected(Node n) { Set<String> emptySet = Collections.emptySet(); return canBeSideEffected(n, emptySet); } /** * @param knownConstants A set of names known to be constant value at * node 'n' (such as locals that are last written before n can execute). * @return Whether the tree can be affected by side-effects or * has side-effects. */ static boolean canBeSideEffected(Node n, Set<String> knownConstants) { switch (n.getType()) { case Token.CALL: case Token.NEW: // Function calls or constructor can reference changed values. // TODO(johnlenz): Add some mechanism for determining that functions // are unaffected by side effects. return true;

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> case Token.NAME: // Non-constant names values may have been changed. return !isConstantName(n) && !knownConstants.contains(n.getString()); // Properties on constant NAMEs can still be side-effected. case Token.GETPROP: case Token.GETELEM: return true; case Token.FUNCTION: // Function expression are not changed by side-effects, // and function declarations are not part of expressions. Preconditions.checkState(isFunctionExpression(n)); return false; } for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (canBeSideEffected(c, knownConstants)) { return true; } } return false; } /* * 0 comma , * 1 assignment = += -= *= /= %= <<= >>= >>>= &= ^= |= * 2 conditional ?: * 3 logical-or || * 4 logical-and && * 5 bitwise-or | * 6 bitwise-xor ^ * 7 bitwise-and & * 8 equality == != * 9 relational < <= > >= * 10 bitwise shift << >> >>> * 11 addition/subtraction + - * 12 multiply/divide * / % * 13 negation/increment ! ~ - ++ -- * 14 call, member () [] . */ static int precedence(int type) { switch (type) { case Token.COMMA: return 0; case Token.ASSIGN_BITOR: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITAND: case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.ASSIGN_URSH: case Token.ASSIGN_ADD: case Token.ASSIGN_SUB: case Token.ASSIGN_MUL: case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: case Token.ASSIGN: return 1; case Token.HOOK: return 2; // ?: operator case Token.OR: return 3; case Token.AND: return 4; case Token.BITOR: return 5; case Token.BITXOR: return

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> == Token.SCRIPT || n.getType() == Token.BLOCK; } /** * @return Whether the node is used as a statement. */ static boolean isStatement(Node n) { Node parent = n.getParent(); // It is not possible to determine definitely if a node is a statement // or not if it is not part of the AST. A FUNCTION node can be // either part of an expression or a statement. Preconditions.checkState(parent != null); switch (parent.getType()) { case Token.SCRIPT: case Token.BLOCK: case Token.LABEL: return true; default: return false; } } /** Whether the node is part of a switch statement. */ static boolean isSwitchCase(Node n) { return n.getType() == Token.CASE || n.getType() == Token.DEFAULT; } /** * @return Whether the name is a reference to a variable, function or * function parameter (not a label or a empty function expression name). */ static boolean isReferenceName(Node n) { return isName(n) && !n.getString().isEmpty(); } /** @return Whether the node is a label name. */ static boolean isLabelName(Node n) { return (n != null && n.getType() == Token.LABEL_NAME); } /** Whether the child node is the FINALLY block of a try. */ static boolean isTryFinallyNode(Node parent, Node child) { return parent.getType() == Token.TRY && parent.getChildCount() == 3 && child == parent.getLastChild(); } /** Safely remove children while maintaining a valid node structure. */ static void removeChild(Node parent, Node node) { // Node parent = node.getParent(); if (isStatementBlock(parent) || isSwitchCase(node) || isTryFinallyNode(parent, node)) { // A statement in a block can simply be removed. parent.removeChild(node); } else if (parent.getType() == Token.VAR) { if (parent.hasMoreThanOneChild()) { parent.removeChild(node); } else { // Remove the node from the parent, so it can be reused. parent.removeChild(node); // This

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> node * @return whether the given node is a function expression that is empty */ static boolean isEmptyFunctionExpression(Node node) { return isFunctionExpression(node) && isEmptyBlock(node.getLastChild()); } /** * Determines if a function takes a variable number of arguments by * looking for references to the "arguments" var_args object. */ static boolean isVarArgsFunction(Node function) { Preconditions.checkArgument(isFunction(function)); return isNameReferenced( function.getLastChild(), "arguments", new MatchNotFunction()); } /** * @return Whether node is a call to methodName. * a.f(...) * a['f'](...) */ static boolean isObjectCallMethod(Node callNode, String methodName) { if (callNode.getType() == Token.CALL) { Node functionIndentifyingExpression = callNode.getFirstChild(); if (isGet(functionIndentifyingExpression)) { Node last = functionIndentifyingExpression.getLastChild(); if (last != null && last.getType() == Token.STRING) { String propName = last.getString(); return (propName.equals(methodName)); } } } return false; } /** * @return Whether the callNode represents an expression in the form of: * x.call(...) * x['call'](...) */ static boolean isFunctionObjectCall(Node callNode) { return isObjectCallMethod(callNode, "call"); } /** * @return Whether the callNode represents an expression in the form of: * x.apply(...) * x['apply'](...) */ static boolean isFunctionObjectApply(Node callNode) { return isObjectCallMethod(callNode, "apply"); } /** * @return Whether the callNode represents an expression in the form of: * x.call(...) * x['call'](...) * where x is a NAME node. */ static boolean isSimpleFunctionObjectCall(Node callNode) { if (isFunctionObjectCall(callNode)) { if (callNode.getFirstChild().getFirstChild().getType() == Token.NAME) { return true; } } return false; } /** * Determines whether this node

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>(Node node, int type) { return containsType(node, type, Predicates.<Node>alwaysTrue()); } /** * Given a node tree, finds all the VAR declarations in that tree that are * not in an inner scope. Then adds a new VAR node at the top of the current * scope that redeclares them, if necessary. */ static void redeclareVarsInsideBranch(Node branch) { Collection<Node> vars = getVarsDeclaredInBranch(branch); if (vars.isEmpty()) { return; } Node parent = getAddingRoot(branch); for (Node nameNode : vars) { Node var = new Node( Token.VAR, Node.newString(Token.NAME, nameNode.getString()) .copyInformationFrom(nameNode)) .copyInformationFrom(nameNode); copyNameAnnotations(nameNode, var.getFirstChild()); parent.addChildToFront(var); } } /** * Copy any annotations that follow a named value. * @param source * @param destination */ static void copyNameAnnotations(Node source, Node destination) { if (source.getBooleanProp(Node.IS_CONSTANT_NAME)) { destination.putBooleanProp(Node.IS_CONSTANT_NAME, true); } } /** * Gets a Node at the top of the current scope where we can add new var * declarations as children. */ private static Node getAddingRoot(Node n) { Node addingRoot = null; Node ancestor = n; while (null != (ancestor = ancestor.getParent())) { int type = ancestor.getType(); if (type == Token.SCRIPT) { addingRoot = ancestor; break; } else if (type == Token.FUNCTION) { addingRoot = ancestor.getLastChild(); break; } } // make sure that the adding root looks ok Preconditions.checkState(addingRoot.getType() == Token.BLOCK || addingRoot.getType() == Token.SCRIPT); Preconditions.checkState(addingRoot.getFirstChild() == null || addingRoot.getFirstChild().getType() != Token.SCRIPT); return addingRoot; } /** Creates function name(params_0, ..., params_n) { body }. */ public static Node newFunctionNode

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> == Token.NAME) { Node parent = n.getParent(); if (parent != null && parent.getType() == Token.VAR) { String name = n.getString(); if (!vars.containsKey(name)) { vars.put(name, n); } } } } } /** * Retrieves vars declared in the current node tree, excluding descent scopes. */ public static Collection<Node> getVarsDeclaredInBranch(Node root) { VarCollector collector = new VarCollector(); visitPreOrder( root, collector, new MatchNotFunction()); return collector.vars.values(); } /** * @return {@code true} if the node an assignment to a prototype property of * some constructor. */ static boolean isPrototypePropertyDeclaration(Node n) { if (!isExprAssign(n)) { return false; } return isPrototypeProperty(n.getFirstChild().getFirstChild()); } static boolean isPrototypeProperty(Node n) { String lhsString = n.getQualifiedName(); if (lhsString == null) { return false; } int prototypeIdx = lhsString.indexOf(".prototype."); return prototypeIdx != -1; } /** * @return The class name part of a qualified prototype name. */ static Node getPrototypeClassName(Node qName) { Node cur = qName; while (isGetProp(cur)) { if (cur.getLastChild().getString().equals("prototype")) { return cur.getFirstChild(); } else { cur = cur.getFirstChild(); } } return null; } /** * @return The string property name part of a qualified prototype name. */ static String getPrototypePropertyName(Node qName) { String qNameStr = qName.getQualifiedName(); int prototypeIdx = qNameStr.lastIndexOf(".prototype."); int memberIndex = prototypeIdx + ".prototype".length() + 1; return qNameStr.substring(memberIndex); } /** * Create a node for an empty result expression: * "void 0" */ static Node newUndefinedNode(Node srcReferenceNode) { // TODO(johnlenz): Why this instead of the more common "undefined"? Node node = new Node(

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>Token.VOID, Node.newNumber(0)); if (srcReferenceNode != null) { node.copyInformationFromForTree(srcReferenceNode); } return node; } /** * Create a VAR node containing the given name and initial value expression. */ static Node newVarNode(String name, Node value) { Node nodeName = Node.newString(Token.NAME, name); if (value != null) { Preconditions.checkState(value.getNext() == null); nodeName.addChildToBack(value); nodeName.copyInformationFrom(value); } Node var = new Node(Token.VAR, nodeName) .copyInformationFrom(nodeName); return var; } /** * A predicate for matching name nodes with the specified node. */ private static class MatchNameNode implements Predicate<Node>{ final String name; MatchNameNode(String name){ this.name = name; } public boolean apply(Node n) { return n.getType() == Token.NAME && n.getString().equals(name); } } /** * A predicate for matching nodes with the specified type. */ static class MatchNodeType implements Predicate<Node>{ final int type; MatchNodeType(int type){ this.type = type; } public boolean apply(Node n) { return n.getType() == type; } } /** * A predicate for matching var or function declarations. */ static class MatchDeclaration implements Predicate<Node> { public boolean apply(Node n) { return isFunctionDeclaration(n) || n.getType() == Token.VAR; } } /** * A predicate for matching anything except function nodes. */ static class MatchNotFunction implements Predicate<Node>{ public boolean apply(Node n) { return !isFunction(n); } } /** * A predicate for matching statements without exiting the current scope. */ static class MatchShallowStatement implements Predicate<Node>{ public boolean apply(Node n) { Node parent = n.getParent(); return n.getType() == Token.BLOCK || (!isFunction(n) && (parent == null || isControlStructure(parent) || isStatementBlock(parent))); } } /** * Finds the number

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>.FUNCTION); return fnNode.getFirstChild().getNext(); } /** * Returns true if a name node represents a constant variable. * * <p>Determining whether a variable is constant has three steps: * <ol> * <li>In CodingConventionAnnotator, any name that matches the * {@link CodingConvention#isConstant(String)} is annotated with an * IS_CONSTANT_NAME property. * <li>The normalize pass renames any variable with the IS_CONSTANT_NAME * annotation and that is initialized to a constant value with * a variable name inlucding $$constant. * <li>Return true here if the variable includes $$constant in its name. * </ol> * * @param node A NAME or STRING node * @return True if the variable is constant */ static boolean isConstantName(Node node) { return node.getBooleanProp(Node.IS_CONSTANT_NAME); } /** Whether the given name is constant by coding convention. */ static boolean isConstantByConvention( CodingConvention convention, Node node, Node parent) { String name = node.getString(); if (parent.getType() == Token.GETPROP && node == parent.getLastChild()) { return convention.isConstantKey(name); } else if (isObjectLitKey(node, parent)) { return convention.isConstantKey(name); } else { return convention.isConstant(name); } } /** * @param nameNode A name node * @return The JSDocInfo for the name node */ static JSDocInfo getInfoForNameNode(Node nameNode) { JSDocInfo info = null; Node parent = null; if (nameNode != null) { info = nameNode.getJSDocInfo(); parent = nameNode.getParent(); } if (info == null && parent != null && ((parent.getType() == Token.VAR && parent.hasOneChild()) || parent.getType() == Token.FUNCTION)) { info = parent.getJSDocInfo(); } return info; } /** * Get the JSDocInfo for a function. */ static JSDocInfo getFunctionInfo(Node n) { Preconditions.

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>/* * Copyright 2009 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Preconditions; import com.google.javascript.jscomp.AbstractCompiler; import com.google.javascript.jscomp.CompilerPass; import com.google.javascript.jscomp.NodeTraversal; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.jscomp.NodeUtil; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.jstype.TernaryValue; /** * Transform the structure of the AST so that the number of explicit exits * are minimized. * * @author johnlenz@google.com (John Lenz) */ class MinimizeExitPoints extends AbstractPostOrderCallback implements CompilerPass { AbstractCompiler compiler; MinimizeExitPoints(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.LABEL: tryMinimizeExits( n.getLastChild(), Token.BREAK, n.getFirstChild().getString()); break; case Token.FOR: case Token.WHILE: tryMinimizeExits( NodeUtil.getLoopCodeBlock(n), Token.CONTINUE, null); break; case Token.DO

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>ifNode.getNext() != null) { // Move siblings of the if block into the opposite // logic block of the exit. Node newDestBlock = new Node(Token.BLOCK).copyInformationFrom(ifNode); if (destBlock == null) { // Only possible if this is the false block. ifNode.addChildToBack(newDestBlock); } else if (destBlock.getType() == Token.EMPTY) { // Use the new block. ifNode.replaceChild(destBlock, newDestBlock); } else if (destBlock.getType() == Token.BLOCK) { // Reuse the existing block. newDestBlock = destBlock; } else { // Add the existing statement to the new block. ifNode.replaceChild(destBlock, newDestBlock); newDestBlock.addChildToBack(destBlock); } // Move all the if node's following siblings. moveAllFollowing(ifNode, ifNode.getParent(), newDestBlock); } // Get rid of the "exit", replace with an empty item if needed. NodeUtil.removeChild(exitNodeParent, exitNode); compiler.reportCodeChange(); } /** * Determines if n matches the type and name for the following types of * "exits": * - return without values * - continues and breaks with or without names. * @param n The node to inspect. * @param type The Token type to look for. * @param labelName The name that must be associated with the exit type. * @nullable labelName non-null only for breaks associated with labels. * @return Whether the node matches the specified block-exit type. */ static private boolean matchingExitNode(Node n, int type, String labelName) { if (n.getType() == type) { if (type == Token.RETURN) { // only returns without expressions. return !n.hasChildren(); } else { if (labelName == null) { return !n.hasChildren(); } else { return n.hasChildren() && labelName.equals(n.getFirstChild().getString()); } } } return false; } /** * Move all the child nodes following start in srcParent to the end of

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>SERVE_BLOCK); // second child contains the catch block, or nothing if there // isn't a catch block Node catchblock = first.getNext().getFirstChild(); if (catchblock != null) { add(catchblock); } if (childCount == 3) { add("finally"); add(last, Context.PRESERVE_BLOCK); } break; } case Token.CATCH: Preconditions.checkState(childCount == 2); add("catch("); add(first); add(")"); add(last, Context.PRESERVE_BLOCK); break; case Token.THROW: Preconditions.checkState(childCount == 1); add("throw"); add(first); // Must have a ';' after a throw statement, otherwise safari can't // parse this. cc.endStatement(true); break; case Token.RETURN: add("return"); if (childCount == 1) { add(first); } else { Preconditions.checkState(childCount == 0); } cc.endStatement(); break; case Token.VAR: if (first != null) { add("var "); addList(first, false, getContextForNoInOperator(context)); } break; case Token.LABEL_NAME: Preconditions.checkState(!n.getString().isEmpty()); addIdentifier(n.getString()); break; case Token.NAME: if (first == null || first.getType() == Token.EMPTY) { addIdentifier(n.getString()); } else { Preconditions.checkState(childCount == 1); addIdentifier(n.getString()); cc.addOp("=", true); if (first.getType() == Token.COMMA) { addExpr(first, NodeUtil.precedence(Token.ASSIGN)); } else { // Add expression, consider nearby code at lowest level of // precedence. addExpr(first, 0, getContextForNoInOperator(context)); } } break; case Token.ARRAYLIT: add("["); addList(first, (int[]) n.getProp(Node.SKIP_INDEXES_PROP)); add("]"); break; case Token.LP: add("

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>("); addList(first); add(")"); break; case Token.COMMA: Preconditions.checkState(childCount == 2); addList(first, false, context); break; case Token.NUMBER: Preconditions.checkState(childCount == 0); cc.addNumber(n.getDouble()); break; case Token.TYPEOF: case Token.VOID: case Token.NOT: case Token.BITNOT: case Token.POS: case Token.NEG: { // All of these unary operators are right-associative Preconditions.checkState(childCount == 1); cc.addOp(NodeUtil.opToStrNoFail(type), false); addExpr(first, NodeUtil.precedence(type)); break; } case Token.HOOK: { Preconditions.checkState(childCount == 3); int p = NodeUtil.precedence(type); addLeftExpr(first, p + 1, context); cc.addOp("?", true); addExpr(first.getNext(), 1); cc.addOp(":", true); addExpr(last, 1); break; } case Token.REGEXP: if (first.getType() != Token.STRING || last.getType() != Token.STRING) { throw new Error("Expected children to be strings"); } String regexp = regexpEscape(first.getString(), outputCharsetEncoder); // I only use one .add because whitespace matters if (childCount == 2) { add(regexp + last.getString()); } else { Preconditions.checkState(childCount == 1); add(regexp); } break; case Token.GET_REF: add(first); break; case Token.REF_SPECIAL: Preconditions.checkState(childCount == 1); add(first); add("."); add((String) n.getProp(Node.NAME_PROP)); break; case Token.FUNCTION: if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } Preconditions.checkState(childCount == 3); boolean funcNeedsParens = (context == Context.START_OF_EXPR); if (func

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>checkState(childCount == 2); add("do"); addNonEmptyStatement(first, Context.OTHER, false); add("while("); add(last); add(")"); cc.endStatement(); break; case Token.WHILE: Preconditions.checkState(childCount == 2); add("while("); add(first); add(")"); addNonEmptyStatement( last, getContextForNonEmptyExpression(context), false); break; case Token.EMPTY: Preconditions.checkState(childCount == 0); break; case Token.GETPROP: { Preconditions.checkState( childCount == 2, "Bad GETPROP: expected 2 children, but got %s", childCount); Preconditions.checkState( last.getType() == Token.STRING, "Bad GETPROP: RHS should be STRING"); boolean needsParens = (first.getType() == Token.NUMBER); if (needsParens) { add("("); } addLeftExpr(first, NodeUtil.precedence(type), context); if (needsParens) { add(")"); } add("."); addIdentifier(last.getString()); break; } case Token.GETELEM: Preconditions.checkState( childCount == 2, "Bad GETELEM: expected 2 children but got %s", childCount); addLeftExpr(first, NodeUtil.precedence(type), context); add("["); add(first.getNext()); add("]"); break; case Token.WITH: Preconditions.checkState(childCount == 2); add("with("); add(first); add(")"); addNonEmptyStatement( last, getContextForNonEmptyExpression(context), false); break; case Token.INC: case Token.DEC: { Preconditions.checkState(childCount == 1); String o = type == Token.INC ? "++" : "--"; int postProp = n.getIntProp(Node.INCRDECR_PROP); // A non-zero post-prop value indicates a post inc/dec, default of zero // is a pre-inc/dec. if (postProp != 0) { addLeft

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>Name(type)); break; case Token.CONTINUE: Preconditions.checkState(childCount <= 1); add("continue"); if (childCount == 1) { if (first.getType() != Token.LABEL_NAME) { throw new Error("Unexpected token type. Should be LABEL_NAME."); } add(" "); add(first); } cc.endStatement(); break; case Token.DEBUGGER: Preconditions.checkState(childCount == 0); add("debugger"); cc.endStatement(); break; case Token.BREAK: Preconditions.checkState(childCount <= 1); add("break"); if (childCount == 1) { if (first.getType() != Token.LABEL_NAME) { throw new Error("Unexpected token type. Should be LABEL_NAME."); } add(" "); add(first); } cc.endStatement(); break; case Token.EXPR_VOID: throw new Error("Unexpected EXPR_VOID. Should be EXPR_RESULT."); case Token.EXPR_RESULT: Preconditions.checkState(childCount == 1); add(first, Context.START_OF_EXPR); cc.endStatement(); break; case Token.NEW: add("new "); int precedence = NodeUtil.precedence(type); // If the first child contains a CALL, then claim higher precedence // to force parentheses. Otherwise, when parsed, NEW will bind to the // first viable parentheses (don't traverse into functions). if (NodeUtil.containsType(first, Token.CALL, new MatchNotFunction())) { precedence = NodeUtil.precedence(first.getType()) + 1; } addExpr(first, precedence); // '()' is optional when no arguments are present Node next = first.getNext(); if (next != null) { add("("); addList(next); add(")"); } break; case Token.STRING: Preconditions.checkState(childCount == 0); add(jsString(n.getString(), outputCharsetEncoder)); break; case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>.OBJECTLIT: { Preconditions.checkState(childCount % 2 == 0); boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext().getNext()) { if (c != first) { cc.listSeparator(); } // Object literal property names don't have to be quoted if they are // not JavaScript keywords if (c.getType() == Token.STRING && !TokenStream.isKeyword(c.getString()) && TokenStream.isJSIdentifier(c.getString()) && // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(c.getString())) { add(c.getString()); } else { addExpr(c, 1); } add(":"); addExpr(c.getNext(), 1); } add("}"); if (needsParens) { add(")"); } break; } case Token.SWITCH: add("switch("); add(first); add(")"); cc.beginBlock(); addAllSiblings(first.getNext()); cc.endBlock(context == Context.STATEMENT); break; case Token.CASE: Preconditions.checkState(childCount == 2); add("case "); add(first); addCaseBody(last); break; case Token.DEFAULT: Preconditions.checkState(childCount == 1); add("default"); addCaseBody(first); break; case Token.LABEL: Preconditions.checkState(childCount == 2); if (first.getType() != Token.LABEL_NAME) { throw new Error("Unexpected token type. Should be LABEL_NAME."); } add(first); add(":"); addNonEmptyStatement( last, getContextForNonEmptyExpression(context), true); break; // This node is auto generated in anonymous functions and should just get // ignored for our purposes. case Token.SETNAME: break; default: throw new Error("Unknown type " + type + "\n" + n.toStringTree()); }

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> cc.endSourceMapping(n); } /** * @return Whether the name is an indirect eval. */ private boolean isIndirectEval(Node n) { return n.getType() == Token.NAME && "eval".equals(n.getString()) && !n.getBooleanProp(Node.DIRECT_EVAL); } /** * Adds a block or expression, substituting a VOID with an empty statement. * This is used for "for (...);" and "if (...);" type statements. * * @param n The node to print. * @param context The context to determine how the node should be printed. */ private void addNonEmptyStatement( Node n, Context context, boolean allowNonBlockChild) { Node nodeToProcess = n; if (!allowNonBlockChild && n.getType() != Token.BLOCK) { throw new Error("Missing BLOCK child."); } // Strip unneeded blocks, that is blocks with <2 children unless // the CodePrinter specifically wants to keep them. if (n.getType() == Token.BLOCK) { int count = getNonEmptyChildCount(n, 2); if (count == 0) { if (cc.shouldPreserveExtraBlocks()) { cc.beginBlock(); cc.endBlock(cc.breakAfterBlockFor(n, context == Context.STATEMENT)); } else { cc.endStatement(true); } return; } if (count == 1) { // Hack around a couple of browser bugs: // Safari needs a block around function declarations. // IE6/7 needs a block around DOs. Node firstAndOnlyChild = getFirstNonEmptyChild(n); boolean alwaysWrapInBlock = cc.shouldPreserveExtraBlocks(); if (alwaysWrapInBlock || isOneExactlyFunctionOrDo(firstAndOnlyChild)) { cc.beginBlock(); add(firstAndOnlyChild, Context.STATEMENT); cc.maybeLineBreak(); cc.endBlock(cc.breakAfterBlockFor(n, context == Context.STATEMENT)); return; } else { // Continue with the only child. nodeToProcess = firstAndOnlyChild; } } if (count > 1) { context = Context.PRESERVE_

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> number affects JavaScript semantics as detailed * in the overview documentation. * * @return an integer that is one of VERSION_1_0, VERSION_1_1, etc. */ public final int getLanguageVersion() { return version; } /** * Set the language version. * * <p> * Setting the language version will affect functions and scripts compiled * subsequently. See the overview documentation for version-specific * behavior. * * @param version the version as specified by VERSION_1_0, VERSION_1_1, etc. */ public void setLanguageVersion(int version) { if (sealed) onSealedMutation(); checkLanguageVersion(version); Object listeners = propertyListeners; if (listeners != null && version != this.version) { firePropertyChangeImpl(listeners, languageVersionProperty, new Integer(this.version), new Integer(version)); } this.version = version; } public static boolean isValidLanguageVersion(int version) { switch (version) { case VERSION_DEFAULT: case VERSION_1_0: case VERSION_1_1: case VERSION_1_2: case VERSION_1_3: case VERSION_1_4: case VERSION_1_5: case VERSION_1_6: return true; } return false; } public static void checkLanguageVersion(int version) { if (isValidLanguageVersion(version)) { return; } throw new IllegalArgumentException("Bad language version: "+version); } /** * Get the implementation version. * * <p> * The implementation version is of the form * <pre> * "<i>name langVer</i> <code>release</code> <i>relNum date</i>" * </pre> * where <i>name</i> is the name of the product, <i>langVer</i> is * the language version, <i>relNum</i> is the release number, and * <i>date</i> is the release date for that specific * release in the form "yyyy mm dd". * * @return a string that encodes the product, language version, release *

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>p> * @param key the key used to index the value * @param value the value to save */ public final void putThreadLocal(Object key, Object value) { if (sealed) onSealedMutation(); if (hashtable == null) hashtable = new Hashtable<Object, Object>(); hashtable.put(key, value); } /** * Remove values from thread-local storage. * @param key the key for the entry to remove. * @since 1.5 release 2 */ public final void removeThreadLocal(Object key) { if (sealed) onSealedMutation(); if (hashtable == null) return; hashtable.remove(key); } /** * @deprecated * @see #FEATURE_DYNAMIC_SCOPE * @see #hasFeature(int) */ @Deprecated public final boolean hasCompileFunctionsWithDynamicScope() { return compileFunctionsWithDynamicScopeFlag; } /** * @deprecated * @see #FEATURE_DYNAMIC_SCOPE * @see #hasFeature(int) */ @Deprecated public final void setCompileFunctionsWithDynamicScope(boolean flag) { if (sealed) onSealedMutation(); compileFunctionsWithDynamicScopeFlag = flag; } /** * Return the debugger context data associated with current context. * @return the debugger data, or null if debugger is not attached */ public final Object getDebuggerContextData() { return debuggerData; } /** * Implementation of {@link Context#hasFeature(int featureIndex)}. * This can be used to customize {@link Context} without introducing * additional subclasses. */ protected boolean hasFeature(int featureIndex) { int version; switch (featureIndex) { case Context.FEATURE_NON_ECMA_GET_YEAR: /* * During the great date rewrite of 1.3, we tried to track the * evolving ECMA standard, which then had a definition of * getYear which always subtracted 1900. Which we * implemented, not realizing that it was incompatible with * the old behavior... now, rather than thrash the behavior * yet again, we've decided to leave it with the - 1

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>900 * behavior and point people to the getFullYear method. But * we try to protect existing scripts that have specified a * version... */ version = getLanguageVersion(); return (version == Context.VERSION_1_0 || version == Context.VERSION_1_1 || version == Context.VERSION_1_2); case Context.FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME: return false; case Context.FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER: return false; case Context.FEATURE_TO_STRING_AS_SOURCE: version = getLanguageVersion(); return version == Context.VERSION_1_2; case Context.FEATURE_PARENT_PROTO_PROPRTIES: return true; case Context.FEATURE_E4X: version = getLanguageVersion(); return (version == Context.VERSION_DEFAULT || version >= Context.VERSION_1_6); case Context.FEATURE_DYNAMIC_SCOPE: return false; case Context.FEATURE_STRICT_VARS: return false; case Context.FEATURE_STRICT_EVAL: return false; case FEATURE_STRICT_MODE: // Make JSCompiler run in non=strict mode always. return false; case FEATURE_WARNING_AS_ERROR: // Let warnings stay warnings for now. return false; } // It is a bug to call the method with unknown featureIndex throw new IllegalArgumentException(String.valueOf(featureIndex)); } /** * Get/Set threshold of executed instructions counter that triggers call to * <code>observeInstructionCount()</code>. * When the threshold is zero, instruction counting is disabled, * otherwise each time the run-time executes at least the threshold value * of script instructions, <code>observeInstructionCount()</code> will * be called. */ public final int getInstructionObserverThreshold() { return instructionThreshold; } public final void setInstructionObserverThreshold(int threshold) { if (sealed) onSealedMutation(); if (threshold < 0) throw new IllegalArgumentException(); instructionThreshold = threshold; } /********** end of API **********/ static Context getContext() { Context cx = getCurrentContext(); if (cx == null) { throw new RuntimeException( "No Context associated with current Thread

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>"); } return cx; } final boolean isVersionECMA1() { return version == VERSION_DEFAULT || version >= VERSION_1_3; } static String getSourcePositionFromStack(int[] linep) { Context cx = getCurrentContext(); if (cx == null) return null; /** * A bit of a hack, but the only way to get filename and line * number from an enclosing frame. */ CharArrayWriter writer = new CharArrayWriter(); RuntimeException re = new RuntimeException(); re.printStackTrace(new PrintWriter(writer)); String s = writer.toString(); int open = -1; int close = -1; int colon = -1; for (int i=0; i < s.length(); i++) { char c = s.charAt(i); if (c == ':') colon = i; else if (c == '(') open = i; else if (c == ')') close = i; else if (c == '\n' && open != -1 && close != -1 && colon != -1 && open < colon && colon < close) { String fileStr = s.substring(open + 1, colon); if (!fileStr.endsWith(".java")) { String lineStr = s.substring(colon + 1, close); try { linep[0] = Integer.parseInt(lineStr); if (linep[0] < 0) { linep[0] = 0; } return fileStr; } catch (NumberFormatException e) { // fall through } } open = close = colon = -1; } } return null; } public final boolean isGeneratingDebugChanged() { return generatingDebugChanged; } /** * Add a name to the list of names forcing the creation of real * activation objects for functions. * * @param name the name of the object to add to the list */ public void addActivationName(String name) { if (sealed) onSealedMutation(); if (activationNames == null) activationNames = new Hashtable<Object, Object>(5); activationNames.put(name, name); } /** *

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> Check whether the name is in the list of names of objects * forcing the creation of activation objects. * * @param name the name of the object to test * * @return true if an function activation object is needed. */ public final boolean isActivationNeeded(String name) { return activationNames != null && activationNames.containsKey(name); } /** * Remove a name from the list of names forcing the creation of real * activation objects for functions. * * @param name the name of the object to remove from the list */ public void removeActivationName(String name) { if (sealed) onSealedMutation(); if (activationNames != null) activationNames.remove(name); } private static String implementationVersion; private boolean sealed; private Object sealKey; // for Objects, Arrays to tag themselves as being printed out, // so they don't print themselves out recursively. // Use ObjToIntMap instead of java.util.HashSet for JDK 1.1 compatibility ObjToIntMap iterating; Object interpreterSecurityDomain; int version; private ErrorReporter errorReporter; private Locale locale; private boolean generatingDebug; private boolean generatingDebugChanged; private boolean generatingSource=true; boolean compileFunctionsWithDynamicScopeFlag; boolean useDynamicScope; private Object debuggerData; private int enterCount; private int optimizationLevel; private Object propertyListeners; private Hashtable<Object, Object> hashtable; /** * This is the list of names of objects forcing the creation of * function activation records. */ Hashtable<Object, Object> activationNames; // For the interpreter to store the last frame for error reports etc. Object lastInterpreterFrame; // For the interpreter to store information about previous invocations // interpreter invocations ObjArray previousInterpreterInvocations; // For instruction counting (interpreter only) int instructionCount; int instructionThreshold; // It can be used to return the second index-like result from function int scratchIndex; // It can be used to return the second uint32 result from function long scratchUint32; }

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>: * * <pre> * class MyCommandLineRunner extends * AbstractCommandLineRunner<MyCompiler, MyOptions> { * MyCommandLineRunner(String[] args) { * super(args); * } * * &#064;Override * protected MyOptions createOptions() { * MyOptions options = new MyOptions(); * CompilerFlagTranslator.setOptionsFromFlags(options); * addMyCrazyCompilerPassThatOutputsAnExtraFile(options); * return options; * } * * &#064;Override * protected MyCompiler createCompiler() { * return new MyCompiler(); * } * * public static void main(String[] args) { * (new MyCommandLineRunner(args)).run(); * } * } * </pre> * * @author bolinfest@google.com (Michael Bolin) */ abstract class AbstractCommandLineRunner<A extends Compiler, B extends CompilerOptions> { private final CommandLineConfig config; private Appendable out; private final PrintStream err; private A compiler; private Charset inputCharset; private String outputCharset; private boolean testMode = false; private Supplier<List<JSSourceFile>> externsSupplierForTesting = null; private Supplier<List<JSSourceFile>> inputsSupplierForTesting = null; private Supplier<List<JSModule>> modulesSupplierForTesting = null; private Function<Integer, Boolean> exitCodeReceiverForTesting = null; // Bookkeeping to measure optimal phase orderings. private static final int NUM_RUNS_TO_DETERMINE_OPTIMAL_ORDER = 100; private final RunTimeStats runTimeStats = new RunTimeStats(); AbstractCommandLineRunner() { this(System.out, System.err); } AbstractCommandLineRunner(PrintStream out, PrintStream err) { this.config = new CommandLineConfig(); this.out = out; this.err = err; } /** * Put the command line runner into test mode. In test mode, * all outputs will be blackholed. * @param externsSupplier A provider for externs. * @param inputsSupplier A provider for source inputs

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>. * @param modulesSupplier A provider for modules. Only one of inputsSupplier * and modulesSupplier may be non-null. * @param exitCodeReceiver A receiver for the status code that would * have been passed to System.exit in non-test mode. */ @VisibleForTesting void enableTestMode( Supplier<List<JSSourceFile>> externsSupplier, Supplier<List<JSSourceFile>> inputsSupplier, Supplier<List<JSModule>> modulesSupplier, Function<Integer, Boolean> exitCodeReceiver) { Preconditions.checkArgument( inputsSupplier == null ^ modulesSupplier == null); testMode = true; this.externsSupplierForTesting = externsSupplier; this.inputsSupplierForTesting = inputsSupplier; this.modulesSupplierForTesting = modulesSupplier; this.exitCodeReceiverForTesting = exitCodeReceiver; } /** * Returns whether we're in test mode. */ protected boolean isInTestMode() { return testMode; } /** * Get the command line config, so that it can be initialized. */ protected CommandLineConfig getCommandLineConfig() { return config; } /** * Returns the instance of the Compiler to use when {@link #run()} is * called. */ protected abstract A createCompiler(); /** * Returns the instance of the Options to use when {@link #run()} is called. * createCompiler() is called before createOptions(), so getCompiler() * will not return null when createOptions() is called. */ protected abstract B createOptions(); /** * The warning classes that are available from the command-line. */ protected DiagnosticGroups getDiagnosticGroups() { if (compiler == null) { return new DiagnosticGroups(); } return compiler.getDiagnosticGroups(); } /** No longer does anything. */ @Deprecated protected void initOptionsFromFlags(CompilerOptions options) {} /** * Sets options based on the configurations set flags API. * Called during the run() run() method. * If you want to ignore the flags API, or intepret flags your own way, * then you should override this method. */ final protected void setRunOptions(CompilerOptions options) throws FlagUsageException, IOException { DiagnosticGroups diagnosticGroups = getDiagnosticGroups(); diagnosticGroups

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>.setWarningLevels( options, config.jscompError, CheckLevel.ERROR); diagnosticGroups.setWarningLevels( options, config.jscompWarning, CheckLevel.WARNING); diagnosticGroups.setWarningLevels( options, config.jscompOff, CheckLevel.OFF); createDefineReplacements(config.define, options); options.manageClosureDependencies = config.manageClosureDependencies; options.devMode = config.jscompDevMode; options.setCodingConvention(config.codingConvention); options.setSummaryDetailLevel(config.summaryDetailLevel); outputCharset = options.outputCharset = getOutputCharset(); inputCharset = getInputCharset(); if (config.jsOutputFile.length() > 0) { options.jsOutputFile = config.jsOutputFile; } if (config.createSourceMap.length() > 0) { options.sourceMapOutputPath = config.createSourceMap; } options.sourceMapDetailLevel = config.sourceMapDetailLevel; if (!config.variableMapInputFile.equals("")) { options.inputVariableMapSerialized = VariableMap.load(config.variableMapInputFile).toBytes(); } if (!config.propertyMapInputFile.equals("")) { options.inputPropertyMapSerialized = VariableMap.load(config.propertyMapInputFile).toBytes(); } } final protected A getCompiler() { return compiler; } /** * Runs the Compiler and calls System.exit() with the exit status of the * compiler. */ final public void run() { int result = 0; int runs = 1; if (config.computePhaseOrdering) { runs = NUM_RUNS_TO_DETERMINE_OPTIMAL_ORDER; PhaseOptimizer.randomizeLoops(); } try { for (int i = 0; i < runs && result == 0; i++) { runTimeStats.recordStartRun(); result = doRun(); runTimeStats.recordEndRun(); } } catch (AbstractCommandLineRunner.FlagUsageException e) { System.err.println(e.getMessage()); result = -1; } catch (Throwable t) { t.printStackTrace(); result = -2; } if (

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>config.computePhaseOrdering) { runTimeStats.outputBestPhaseOrdering(); } if (testMode) { exitCodeReceiverForTesting.apply(result); } else { System.exit(result); } } /** * Returns the PrintStream for writing errors associated with this * AbstractCommandLineRunner. */ protected PrintStream getErrorPrintStream() { return err; } /** * An exception thrown when command-line flags are used incorrectly. */ protected static class FlagUsageException extends Exception { private static final long serialVersionUID = 1L; FlagUsageException(String message) { super(message); } } /** * Creates inputs from a list of files. * * @param files A list of filenames * @param allowStdIn Whether '-' is allowed appear as a filename to represent * stdin. If true, '-' is only allowed to appear once. * @return An array of inputs */ private List<JSSourceFile> createInputs(List<String> files, boolean allowStdIn) throws FlagUsageException, IOException { List<JSSourceFile> inputs = new ArrayList<JSSourceFile>(files.size()); boolean usingStdin = false; for (String filename : files) { if (!"-".equals(filename)) { JSSourceFile newFile = JSSourceFile.fromFile(filename, inputCharset); inputs.add(newFile); } else { if (!allowStdIn) { throw new FlagUsageException("Can't specify stdin."); } if (usingStdin) { throw new FlagUsageException("Can't specify stdin twice."); } inputs.add(JSSourceFile.fromInputStream("stdin", System.in)); usingStdin = true; } } return inputs; } /** * Creates js source code inputs from a list of files. */ private List<JSSourceFile> createSourceInputs(List<String> files) throws FlagUsageException, IOException { if (isInTestMode()) { return inputsSupplierForTesting.get(); } if (files.isEmpty()) { files = Collections.singletonList("-"); } try { return createInputs(files, true); } catch (FlagUsageException e) {

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> if (compiler != null && compiler.getSourceMap() != null) { compiler.getSourceMap().setWrapperPrefix(prefix); } } else { out.append(code); out.append('\n'); } } /** * Creates any directories necessary to write a file that will have a given * path prefix. */ private static void maybeCreateDirsForPath(String pathPrefix) { if (pathPrefix.length() > 0) { String dirName = pathPrefix.charAt(pathPrefix.length() - 1) == File.separatorChar ? pathPrefix.substring(0, pathPrefix.length() - 1) : new File( pathPrefix).getParent(); if (dirName != null) { new File(dirName).mkdirs(); } } } /** * Parses command-line arguments and runs the compiler. * * @return system exit status */ protected int doRun() throws FlagUsageException, IOException { Compiler.setLoggingLevel(Level.parse(config.loggingLevel)); List<JSSourceFile> externs = createExterns(); compiler = createCompiler(); B options = createOptions(); List<JSModule> modules = null; Result result; setRunOptions(options); boolean writeOutputToFile = !options.jsOutputFile.isEmpty(); if (writeOutputToFile) { out = fileNameToOutputWriter(options.jsOutputFile); } else if (out instanceof OutputStream) { out = streamToOutputWriter((OutputStream) out); } List<String> jsFiles = config.js; List<String> moduleSpecs = config.module; if (!moduleSpecs.isEmpty()) { modules = createJsModules(moduleSpecs, jsFiles); result = compiler.compileModules(externs, modules, options); } else { List<JSSourceFile> inputs = createSourceInputs(jsFiles); result = compiler.compile(externs, inputs, options); } int errCode = processResults(result, modules, options); // Close the output if we are writing to a file. if (out instanceof Closeable) { ((Closeable)out).close(); } return errCode; } /** * Processes the results of the compile job,

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> and returns an error code. */ int processResults(Result result, List<JSModule> modules, B options) throws FlagUsageException, IOException { if (config.computePhaseOrdering) { return 0; } if (config.printPassGraph) { if (compiler.getRoot() == null) { return 1; } else { out.append(DotFormatter.toDot(compiler.getPassConfig().getPassGraph())); out.append('\n'); return 0; } } if (config.printAst) { if (compiler.getRoot() == null) { return 1; } else { ControlFlowGraph<Node> cfg = compiler.computeCFG(); DotFormatter.appendDot(compiler.getRoot(), cfg, out); out.append('\n'); return 0; } } if (config.printTree) { if (compiler.getRoot() == null) { out.append("Code contains errors; no tree was generated.\n"); return 1; } else { compiler.getRoot().appendStringTree(out); out.append("\n"); return 0; } } if (result.success) { if (modules == null) { writeOutput(out, compiler, compiler.toSource(), config.outputWrapper, config.outputWrapperMarker); // Output the source map if requested. outputSourceMap(options); } else { String moduleFilePrefix = config.moduleOutputPathPrefix; maybeCreateDirsForPath(moduleFilePrefix); Map<String, String> moduleWrappers = parseModuleWrappers(config.moduleWrapper, modules); // If the source map path is in fact a pattern for each // module, create a stream per-module. Otherwise, create // a single source map. Writer mapOut = null; if (!shouldGenerateMapPerModule(options)) { mapOut = fileNameToOutputWriter(expandSourceMapPath(options, null)); } for (JSModule m : modules) { if (shouldGenerateMapPerModule(options)) { mapOut = fileNameToOutputWriter(expandSourceMapPath(options, m)); } Writer writer = fileNameToOutputWriter( moduleFilePrefix + m.getName() + ".js");

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> if (options.sourceMapOutputPath != null) { compiler.getSourceMap().reset(); } writeOutput(writer, compiler, compiler.toSource(m), moduleWrappers.get( m.getName()), "%s"); if (options.sourceMapOutputPath != null) { compiler.getSourceMap().appendTo(mapOut, m.getName()); } writer.close(); if (shouldGenerateMapPerModule(options) && mapOut != null) { mapOut.close(); mapOut = null; } } if (mapOut != null) { mapOut.close(); } } // Output the externs if required. if (options.externExportsPath != null) { Writer eeOut = openExternExportsStream(options, options.jsOutputFile); eeOut.append(result.externExport); eeOut.close(); } // Output the variable and property name maps if requested. outputNameMaps(options); // Output the manifest if requested. outputManifest(); } // return 0 if no errors, the error count otherwise return Math.min(result.errors.length, 0x7f); } /** * Query the flag for the input charset, and return a Charset object * representing the selection. * * @return Charset to use when reading inputs * @throws FlagUsageException if flag is not a valid Charset name. */ private Charset getInputCharset() throws FlagUsageException { if (!config.charset.isEmpty()) { if (!Charset.isSupported(config.charset)) { throw new FlagUsageException(config.charset + " is not a valid charset name."); } return Charset.forName(config.charset); } return Charsets.UTF_8; } /** * Query the flag for the output charset. * * Let the outputCharset be the same as the input charset... except if * we're reading in UTF-8 by default. By tradition, we've always * output ASCII to avoid various hiccups with different browsers, * proxies and firewalls. * * @return Name of the charset to use when writing outputs. Guaranteed to * be a supported charset. * @throws FlagUsageException if flag is not a valid

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> Charset name. */ private String getOutputCharset() throws FlagUsageException { if (!config.charset.isEmpty()) { if (!Charset.isSupported(config.charset)) { throw new FlagUsageException(config.charset + " is not a valid charset name."); } return config.charset; } return "US-ASCII"; } protected List<JSSourceFile> createExterns() throws FlagUsageException, IOException { return isInTestMode() ? externsSupplierForTesting.get() : createExternInputs(config.externs); } /** * Returns true if and only if a source map file should be generated for each * module, as opposed to one unified map. This is specified by having the * source map pattern include the %outname% variable. */ private boolean shouldGenerateMapPerModule(B options) { return options.sourceMapOutputPath != null && options.sourceMapOutputPath.contains("%outname%"); } /** * Returns a stream for outputting the generated externs file. * * @param options The options to the Compiler. * @param path The path of the generated JS source file. * * @return The stream or null if no extern-ed exports are being generated. */ private Writer openExternExportsStream(B options, String path) throws IOException { if (options.externExportsPath == null) { return null; } String exPath = options.externExportsPath; if (!exPath.contains(File.separator)) { File outputFile = new File(path); exPath = outputFile.getParent() + File.separatorChar + exPath; } return fileNameToOutputWriter(exPath); } /** * Expand a file path specified on the command-line. * * Most file paths on the command-line allow an %outname% placeholder. * The placeholder will expand to a different value depending on * the current output mode. There are three scenarios: * * 1) Single js output, single extra output: sub in jsOutputPath. * 2) Multiple js output, single extra output: sub in the base module name. * 3) Multiple js output, multiple extra output: sub in the module output file. *

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> * Passing a JSModule to this function automatically triggers case #3. * Otherwise, we'll use strategy #1 or #2 based on the current output mode. */ private String expandCommandLinePath( String path, JSModule forModule) { String sub; if (forModule != null) { sub = config.moduleOutputPathPrefix + forModule.getName() + ".js"; } else if (!config.module.isEmpty()) { sub = config.moduleOutputPathPrefix; } else { sub = config.jsOutputFile; } return path.replace("%outname%", sub); } /** Expansion function for source map. */ @VisibleForTesting String expandSourceMapPath(B options, JSModule forModule) { if (Strings.isEmpty(options.sourceMapOutputPath)) { return null; } return expandCommandLinePath(options.sourceMapOutputPath, forModule); } /** Expansion function for the manifest. */ @VisibleForTesting String expandManifest(JSModule forModule) { if (Strings.isEmpty(config.outputManifest)) { return null; } return expandCommandLinePath(config.outputManifest, forModule); } /** * Converts a file name into a Writer. * Returns null if the file name is null. */ private Writer fileNameToOutputWriter(String fileName) throws IOException { if (fileName == null) { return null; } if (testMode) { return new StringWriter(); } return streamToOutputWriter(new FileOutputStream(fileName)); } /** * Create a writer. */ private Writer streamToOutputWriter(OutputStream stream) throws IOException { if (outputCharset == null) { return new BufferedWriter( new OutputStreamWriter(stream)); } else { return new BufferedWriter( new OutputStreamWriter(stream, outputCharset)); } } /** * Outputs the source map found in the compiler to the proper path if one * exists. * * @param options The options to the Compiler. */ private void outputSourceMap(B options) throws IOException { if (Strings.isEmpty(options.sourceMapOutputPath)) { return; } String outName = expandSourceMapPath(options, null); Writer out = fileNameToOutput

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>Writer(outName); compiler.getSourceMap().appendTo(out, outName); out.close(); } /** * Returns the path at which to output map file(s) based on the path at which * the JS binary will be placed. * * @return The path in which to place the generated map file(s). */ private String getMapPath(String outputFile) { String basePath = ""; if (outputFile.equals("")) { // If we have a js_module_binary rule, output the maps // at modulename_props_map.out, etc. if (!config.moduleOutputPathPrefix.equals("")) { basePath = config.moduleOutputPathPrefix; } else { basePath = "jscompiler"; } } else { // Get the path of the output file. File file = new File(outputFile); String outputFileName = file.getName(); // Strip the .js from the name. if (outputFileName.endsWith(".js")) { outputFileName = outputFileName.substring(0, outputFileName.length() - 3); } basePath = file.getParent() + File.separatorChar + outputFileName; } return basePath; } /** * Outputs the variable and property name maps for the specified compiler if * the proper FLAGS are set. */ private void outputNameMaps(B options) throws FlagUsageException, IOException { String propertyMapOutputPath = null; String variableMapOutputPath = null; String functionInformationMapOutputPath = null; // Check the create_name_map_files FLAG. if (config.createNameMapFiles) { String basePath = getMapPath(options.jsOutputFile); propertyMapOutputPath = basePath + "_props_map.out"; variableMapOutputPath = basePath + "_vars_map.out"; functionInformationMapOutputPath = basePath + "_functions_map.out"; } // Check the individual FLAGS. if (!config.variableMapOutputFile.equals("")) { if (variableMapOutputPath != null) { throw new FlagUsageException("The flags variable_map_output_file and " + "create_name_map_files cannot both be used simultaniously."); } variableMapOutputPath = config.variableMapOutputFile; }

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> if (!config.propertyMapOutputFile.equals("")) { if (propertyMapOutputPath != null) { throw new FlagUsageException("The flags property_map_output_file and " + "create_name_map_files cannot both be used simultaniously."); } propertyMapOutputPath = config.propertyMapOutputFile; } // Output the maps. if (variableMapOutputPath != null) { if (compiler.getVariableMap() != null) { compiler.getVariableMap().save(variableMapOutputPath); } } if (propertyMapOutputPath != null) { if (compiler.getPropertyMap() != null) { compiler.getPropertyMap().save(propertyMapOutputPath); } } if (functionInformationMapOutputPath != null) { if (compiler.getFunctionalInformationMap() != null) { FileOutputStream file = new FileOutputStream(functionInformationMapOutputPath); CodedOutputStream outputStream = CodedOutputStream.newInstance(file); compiler.getFunctionalInformationMap().writeTo(outputStream); outputStream.flush(); file.flush(); file.close(); } } } /** * Create a map of constant names to constant values from a textual * description of the map. * * @param definitions A list of overriding definitions for defines in * the form <name>[=<val>], where <val> is a number, boolean, or * single-quoted string without single quotes. */ @VisibleForTesting static void createDefineReplacements(List<String> definitions, CompilerOptions options) { // Parse the definitions for (String override : definitions) { String[] assignment = override.split("=", 2); String defName = assignment[0]; if (defName.length() > 0) { if (assignment.length == 1) { options.setDefineToBooleanLiteral(defName, true); continue; } else { String defValue = assignment[1]; if (defValue.equals("true")) { options.setDefineToBooleanLiteral(defName, true); continue; } else if (defValue.equals("false")) { options.setDefineToBooleanLiteral(defName, false); continue; } else if (defValue.length() >

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> 1 && ((defValue.charAt(0) == '\'' && defValue.charAt(defValue.length() - 1) == '\'') || (defValue.charAt(0) == '\"' && defValue.charAt(defValue.length() - 1) == '\"'))) { // If the value starts and ends with a single quote, // we assume that it's a string. String maybeStringVal = defValue.substring(1, defValue.length() - 1); if (maybeStringVal.indexOf(defValue.charAt(0)) == -1) { options.setDefineToStringLiteral(defName, maybeStringVal); continue; } } else { try { options.setDefineToDoubleLiteral(defName, Double.parseDouble(defValue)); continue; } catch (NumberFormatException e) { // do nothing, it will be caught at the end } } } } throw new RuntimeException( "--define flag syntax invalid: " + override); } } /** * Returns true if and only if a manifest should be generated for each * module, as opposed to one unified manifest. */ private boolean shouldGenerateManifestPerModule() { return !config.module.isEmpty() && config.outputManifest != null && config.outputManifest.contains("%outname%"); } /** * Writes the manifest of all compiler input files that survived * manage_closure_dependencies, if requested. */ private void outputManifest() throws IOException { String outputManifest = config.outputManifest; if (Strings.isEmpty(outputManifest)) { return; } JSModuleGraph graph = compiler.getModuleGraph(); if (shouldGenerateManifestPerModule()) { // Generate per-module manifests. Iterable<JSModule> modules = graph.getAllModules(); for (JSModule module : modules) { Writer out = fileNameToOutputWriter(expandManifest(module)); printManifestTo(module.getInputs(), out); out.close(); } } else { // Generate a single file manifest. Writer out = fileNameToOutputWriter(expandManifest(null)); if (graph == null) { printManifestTo(compiler.getInputsInOrder(), out); } else { printModuleGraphManifest

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.CALL && GET_CSS_NAME_FUNCTION.equals(n.getFirstChild().getQualifiedName())) { int count = n.getChildCount(); Node first = n.getFirstChild().getNext(); switch (count) { case 2: // Replace the function call with the processed argument. if (first.getType() == Token.STRING) { processStringNode(t, first); n.removeChild(first); parent.replaceChild(n, first); compiler.reportCodeChange(); } else { compiler.report(t.makeError(n, STRING_LITERAL_EXPECTED_ERROR, Token.name(first.getType()))); } break; case 3: // Replace function call with concatenation of two args. It's // assumed the first arg has already been processed. Node second = first.getNext(); if (first.getType() == Token.STRING) { compiler.report(t.makeError( n, UNEXPECTED_STRING_LITERAL_ERROR, first.getString(), second.getString())); } else if (second.getType() == Token.STRING) { processStringNode(t, second); n.removeChild(first); Node replacement = new Node(Token.ADD, first, Node.newString("-" + second.getString()) .copyInformationFrom(second)) .copyInformationFrom(n); replacement.setJSType(nativeStringType); parent.replaceChild(n, replacement); compiler.reportCodeChange(); } else { compiler.report(t.makeError(n, STRING_LITERAL_EXPECTED_ERROR, Token.name(second.getType()))); } break; default: compiler.report(t.makeError( n, INVALID_NUM_ARGUMENTS_ERROR, String.valueOf(count))); } } } /** * Processes a string argument to goog.getCssName(). The string will be * renamed based off the symbol map. If there is no map or any part of the * name can't be renamed, a warning is reported to the compiler and the node * is left unchanged. * * If the type is unexpected then an error is reported to the

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> compiler. * * @param t The node traversal. * @param n The string node to process. */ private void processStringNode(NodeTraversal t, Node n) { if (symbolMap != null || cssNames != null) { String[] parts = n.getString().split("-"); for (int i = 0; i < parts.length; i++) { if (cssNames != null) { Integer count = cssNames.get(parts[i]); if (count == null) { count = Integer.valueOf(0); } cssNames.put(parts[i], count.intValue() + 1); } if (symbolMap != null) { String replacement = symbolMap.get(parts[i]); if (replacement == null) { // If we can't encode all parts, don't encode any of it. compiler.report(t.makeError( n, UNKNOWN_SYMBOL_WARNING, parts[i], n.getString())); return; } parts[i] = replacement; } } if (symbolMap != null) { n.setString(Joiner.on("-").join(parts)); } } } } }

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> null; if (callName.getType() == Token.GETPROP) { methodName = callName.getLastChild().getString(); } else if (callName.getType() == Token.NAME) { String name = callName.getString(); int dollarIndex = name.lastIndexOf('$'); if (dollarIndex != -1) { methodName = name.substring(dollarIndex + 1); } } if (methodName != null) { if (methodName.equals("inherits")) { return SubclassType.INHERITS; } else if (methodName.equals("mixin")) { return SubclassType.MIXIN; } } return null; } @Override public boolean isSuperClassReference(String propertyName) { return "superClass_".equals(propertyName); } /** * Given a qualified name node, strip "prototype" off the end. * * Examples of this transformation: * a.b.c => a.b.c * a.b.c.prototype => a.b.c */ private Node stripPrototype(Node qualifiedName) { if (qualifiedName.getType() == Token.GETPROP && qualifiedName.getLastChild().getString().equals("prototype")) { return qualifiedName.getFirstChild(); } return qualifiedName; } /** * Exctracts X from goog.provide('X'), if the applied Node is goog. * * @return The extracted class name, or null. */ @Override public String extractClassNameIfProvide(Node node, Node parent){ return extractClassNameIfGoog(node, parent, "goog.provide"); } /** * Exctracts X from goog.require('X'), if the applied Node is goog. * * @return The extracted class name, or null. */ @Override public String extractClassNameIfRequire(Node node, Node parent){ return extractClassNameIfGoog(node, parent, "goog.require"); } private static String extractClassNameIfGoog(Node node, Node parent, String functionName){ String className = null; if (NodeUtil.isExprCall(parent)) { Node callee = node.getFirstChild(); if (callee != null && callee.getType() == Token.GETPROP) {

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> String qualifiedName = callee.getQualifiedName(); if ((functionName).equals(qualifiedName)) { className = callee.getNext().getString(); } } } return className; } /** * Use closure's implementation. * @return closure's function name for exporting properties. */ @Override public String getExportPropertyFunction() { return "goog.exportProperty"; } /** * Use closure's implementation. * @return closure's function name for exporting symbols. */ @Override public String getExportSymbolFunction() { return "goog.exportSymbol"; } @Override public List<String> identifyTypeDeclarationCall(Node n) { Node callName = n.getFirstChild(); if ("goog.addDependency".equals(callName.getQualifiedName()) && n.getChildCount() >= 3) { Node typeArray = callName.getNext().getNext(); if (typeArray.getType() == Token.ARRAYLIT) { List<String> typeNames = Lists.newArrayList(); for (Node name = typeArray.getFirstChild(); name != null; name = name.getNext()) { if (name.getType() == Token.STRING) { typeNames.add(name.getString()); } } return typeNames; } } return null; } @Override public String identifyTypeDefAssign(Node n) { Node firstChild = n.getFirstChild(); int type = n.getType(); if (type == Token.ASSIGN) { if (TYPEDEF_NAME.equals(n.getLastChild().getQualifiedName())) { return firstChild.getQualifiedName(); } } else if (type == Token.VAR && firstChild.hasChildren()) { if (TYPEDEF_NAME.equals( firstChild.getFirstChild().getQualifiedName())) { return firstChild.getString(); } } return null; } @Override public String getAbstractMethodName() { return "goog.abstractMethod"; } @Override public String getSingletonGetterClassName(Node callNode) { Node callArg = callNode.getFirstChild(); String callName = callArg.getQualifiedName(); // Use both the original name and the post-CollapseProperties name. if (!("goog.addSingletonGetter".equals(callName

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>_call"; default: Kit.codeBug(); } return null; } private static class NumberNode extends Node { private static final long serialVersionUID = 1L; NumberNode(double number) { super(Token.NUMBER); this.number = number; } public NumberNode(double number, int lineno, int charno) { super(Token.NUMBER, lineno, charno); this.number = number; } @Override public double getDouble() { return this.number; } @Override public void setDouble(double d) { this.number = d; } @Override public boolean isEquivalentTo(Node node) { return (node instanceof NumberNode && getDouble() == ((NumberNode) node).getDouble()); } private double number; } private static class StringNode extends Node { private static final long serialVersionUID = 1L; StringNode(int type, String str) { super(type); if (null == str) { throw new IllegalArgumentException("StringNode: str is null"); } this.str = str; } StringNode(int type, String str, int lineno, int charno) { super(type, lineno, charno); if (null == str) { throw new IllegalArgumentException("StringNode: str is null"); } this.str = str; } /** * returns the string content. * @return non null. */ @Override public String getString() { return this.str; } /** * sets the string content. * @param str the new value. Non null. */ @Override public void setString(String str) { if (null == str) { throw new IllegalArgumentException("StringNode: str is null"); } this.str = str; } @Override public boolean isEquivalentTo(Node node) { return (node instanceof StringNode && this.str.equals(((StringNode) node).str)); } /** * If the property is not defined, this was not a quoted key. The * QUOTED_PROP int property is only assigned to STRING tokens used as * object lit keys. * @return true if this was a quoted string key in an

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>Type, int value) { removeProp(propType); if (value != 0) { propListHead = new PropListItem(propType, value, propListHead); } } // Gets all the property types, in sorted order. private int[] getSortedPropTypes() { int count = 0; for (PropListItem x = propListHead; x != null; x = x.next) { count++; } int[] keys = new int[count]; for (PropListItem x = propListHead; x != null; x = x.next) { count--; keys[count] = x.type; } Arrays.sort(keys); return keys; } public int getLineno() { return extractLineno(sourcePosition); } public int getCharno() { return extractCharno(sourcePosition); } /** Can only be called when <tt>getType() == TokenStream.NUMBER</tt> */ public double getDouble() throws UnsupportedOperationException { if (this.getType() == Token.NUMBER) { throw new IllegalStateException( "Number node not created with Node.newNumber"); } else { throw new UnsupportedOperationException(this + " is not a number node"); } } /** Can only be called when <tt>getType() == TokenStream.NUMBER</tt> */ public void setDouble(double s) throws UnsupportedOperationException { if (this.getType() == Token.NUMBER) { throw new IllegalStateException( "Number node not created with Node.newNumber"); } else { throw new UnsupportedOperationException(this + " is not a string node"); } } /** Can only be called when node has String context. */ public String getString() throws UnsupportedOperationException { if (this.getType() == Token.STRING) { throw new IllegalStateException( "String node not created with Node.newString"); } else { throw new UnsupportedOperationException(this + " is not a string node"); } } /** Can only be called when node has String context. */ public void setString(String s) throws UnsupportedOperationException { if (this.getType() == Token.STRING) { throw new IllegalStateException( "String node not created with Node.newString"); } else { throw new UnsupportedOperationException(this + " is not a string node

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>"); } } @Override public String toString() { return toString(true, true, true); } public String toString( boolean printSource, boolean printAnnotations, boolean printType) { if (Token.printTrees) { StringBuilder sb = new StringBuilder(); toString(sb, printSource, printAnnotations, printType); return sb.toString(); } return String.valueOf(type); } private void toString( StringBuilder sb, boolean printSource, boolean printAnnotations, boolean printType) { if (Token.printTrees) { sb.append(Token.name(type)); if (this instanceof StringNode) { sb.append(' '); sb.append(getString()); } else if (type == Token.FUNCTION) { sb.append(' '); // In the case of JsDoc trees, the first child is often not a string // which causes exceptions to be thrown when calling toString or // toStringTree. if (first.getType() == Token.STRING) { sb.append(first.getString()); } } else if (this instanceof ScriptOrFnNode) { ScriptOrFnNode sof = (ScriptOrFnNode) this; if (this instanceof FunctionNode) { FunctionNode fn = (FunctionNode) this; sb.append(' '); sb.append(fn.getFunctionName()); } if (printSource) { sb.append(" [source name: "); sb.append(sof.getSourceName()); sb.append("] [encoded source length: "); sb.append(sof.getEncodedSourceEnd() - sof.getEncodedSourceStart()); sb.append("] [base line: "); sb.append(sof.getBaseLineno()); sb.append("] [end line: "); sb.append(sof.getEndLineno()); sb.append(']'); } } else if (type == Token.NUMBER) { sb.append(' '); sb.append(getDouble()); } if (printSource) { int lineno = getLineno(); if (lineno != -1) { sb.append(' '); sb.append(lineno); } } if (printAnnotations) { int[] keys = getSortedPropTypes(); for (int i =

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>.BREAK: case Token.CONTINUE: case Token.VAR: case Token.CONST: case Token.WITH: case Token.CATCH: case Token.FINALLY: case Token.BLOCK: case Token.LABEL: case Token.TARGET: case Token.LOOP: case Token.JSR: case Token.SETPROP_OP: case Token.SETELEM_OP: case Token.LOCAL_BLOCK: case Token.SET_REF_OP: return true; default: return false; } } /** * This function takes a set of GETPROP nodes and produces a string that is * each property separated by dots. If the node ultimately under the left * sub-tree is not a simple name, this is not a valid qualified name. * * @return a null if this is not a qualified name, or a dot-separated string * of the name and properties. */ public String getQualifiedName() { if (type == Token.NAME) { return getString(); } else if (type == Token.GETPROP) { String left = getFirstChild().getQualifiedName(); if (left == null) { return null; } return left + "." + getLastChild().getString(); } else if (type == Token.THIS) { return "this"; } else { return null; } } /** * Returns whether a node corresponds to a simple or a qualified name, such as * <code>x</code> or <code>a.b.c</code> or <code>this.a</code>. */ public boolean isQualifiedName() { switch (getType()) { case Token.NAME: case Token.THIS: return true; case Token.GETPROP: return getFirstChild().isQualifiedName(); default: return false; } } /** * Returns whether a node corresponds to a simple or a qualified name without * a "this" reference, such as <code>a.b.c</code>, but not <code>this.a</code> * . */ public boolean isUnscopedQualifiedName() { switch (getType()) { case Token.NAME: return true; case Token.GETPROP: return getFirstChild().is

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> attempt to replace the arguments. The currentArgumentsAccess // is stale and as we exit the Scope, no longer holds all the access to the // current scope anymore. We'll pop the access list from the outer scope // and set it as currentArgumentsAcess if the outer scope is not the global // scope. if (!argumentsAccessStack.isEmpty()) { currentArgumentsAccess = argumentsAccessStack.pop(); } else { currentArgumentsAccess = null; } } @Override public boolean shouldTraverse( NodeTraversal nodeTraversal, Node node, Node parent) { // We will continuously recurse down the AST regardless of the node types. return true; } @Override public void visit(NodeTraversal traversal, Node node, Node parent) { Preconditions.checkNotNull(traversal); Preconditions.checkNotNull(node); // Searches for all the references to the arguments array. // We don't have an arguments list set up for this scope. This implies we // are currently in the global scope so we will not record any arguments // array access. if (currentArgumentsAccess == null) { return; } // Otherwise, we are in a function scope and we should record if the current // name is referring to the implicit arguments array. if (NodeUtil.isName(node) && ARGUMENTS.equals(node.getString())) { currentArgumentsAccess.add(node); } } /** * Tries to optimize all the arguments array access in this scope by assigning * a name to each element. * * @param scope scope of the function * @return true if any modification has been done to the AST */ private boolean tryReplaceArguments(Scope scope) { Node parametersList = scope.getRootNode().getFirstChild().getNext(); Preconditions.checkState(parametersList.getType() == Token.LP); // Keep track of rather this function modified the AST and needs to be // reported back to the compiler later. boolean changed = false; // Number of parameter that can be accessed without using the arguments // array. int numNamedParameter = parametersList.getChildCount(); // We want to guess what the highest index that has been access from the // arguments array. We will guess that it does not use anything index higher // than the named parameter list first until we see

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> the formal parameter to the method's signature. // Example: function() --> function(r0, r1, r2) for (int i = 0; i < numExtraArgs; i++) { String name = getNewName(); argNames[i] = name; parametersList.addChildrenToBack(Node.newString(Token.NAME, name)); changed = true; } // This loop performs the replacement of arguments[x] -> a if x is known. for (Node ref : currentArgumentsAccess) { Node index = ref.getNext(); // Skip if it is unknown. if (index.getType() != Token.NUMBER) { continue; } int value = (int) index.getDouble(); // Unnamed parameter. if (value >= numNamedParameter) { ref.getParent().getParent().replaceChild(ref.getParent(), Node.newString(Token.NAME, argNames[value - numNamedParameter])); } else { // Here, for no apparent reason, the user is accessing a named parameter // with arguments[idx]. We can replace it with the actual name for them. Node name = parametersList.getFirstChild(); // This is a linear search for the actual name from the signature. // It is not necessary to make this fast because chances are the user // will not deliberately write code like this. for (int i = 0; i < value; i++) { name = name.getNext(); } ref.getParent().getParent().replaceChild(ref.getParent(), Node.newString(Token.NAME, name.getString())); } changed = true; } return changed; } /** * Generate a unique name for the next parameter. */ private String getNewName() { return paramPredix + uniqueId++; } }

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> node.getFirstChild(); // Function declarations are skipped since control doesn't go into that // function (unless it is called) while (child != null && child.getType() == Token.FUNCTION) { child = child.getNext(); } if (child != null) { createEdge(node, Branch.UNCOND, computeFallThrough(child)); } else { createEdge(node, Branch.UNCOND, computeFollowNode(node, this)); } // Synthetic blocks if (parent != null) { switch (parent.getType()) { case Token.DEFAULT: case Token.CASE: case Token.TRY: break; default: if (node.getType() == Token.BLOCK && node.isSyntheticBlock()) { createEdge(node, Branch.SYN_BLOCK, computeFollowNode(node, this)); } break; } } } private void handleFunction(Node node) { // A block transfer control to its first child if it is not empty. Preconditions.checkState(node.getChildCount() >= 3); createEdge(node, Branch.UNCOND, computeFallThrough(node.getFirstChild().getNext().getNext())); Preconditions.checkState(exceptionHandler.peek() == node); exceptionHandler.pop(); } private void handleExpr(Node node) { createEdge(node, Branch.UNCOND, computeFollowNode(node, this)); connectToPossibleExceptionHandler(node, node); } private void handleThrow(Node node) { connectToPossibleExceptionHandler(node, node); } private void handleTry(Node node) { createEdge(node, Branch.UNCOND, node.getFirstChild()); } private void handleCatch(Node node) { createEdge(node, Branch.UNCOND, node.getLastChild()); } private void handleBreak(Node node) { String label = null; // See if it is a break with label. if (node.hasChildren()) { label = node.getFirstChild().getString(); } Node cur; Node lastJump; Node parent = node.getParent(); /* * Continuously look up the ancestor tree for the BREAK target or the target * with the corresponding label and connect to it. If along the path we * discover

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> a FINALLY, we will connect the BREAK to that FINALLY. From then * on, we will just record the control flow changes in the finallyMap. This * is due to the fact that we need to connect any node that leaves its own * FINALLY block to the outer FINALLY or the BREAK's target but those nodes * are not known yet due to the way we traverse the nodes. */ for (cur = node, lastJump = node; !isBreakTarget(cur, label); cur = parent, parent = parent.getParent()) { if (cur.getType() == Token.TRY && NodeUtil.hasFinally(cur)) { if (lastJump == node) { createEdge(lastJump, Branch.UNCOND, computeFallThrough( cur.getLastChild())); } else { finallyMap.put(lastJump, computeFallThrough(cur.getLastChild())); } lastJump = cur; } Preconditions.checkState(parent != null, "Cannot find break target."); } if (lastJump == node) { createEdge(lastJump, Branch.UNCOND, computeFollowNode(cur, this)); } else { finallyMap.put(lastJump, computeFollowNode(cur, this)); } } private void handleContinue(Node node) { String label = null; if (node.hasChildren()) { label = node.getFirstChild().getString(); } Node cur; Node lastJump; // Similar to handBreak's logic with a few minor variation. Node parent = node.getParent(); for (cur = node, lastJump = node; !isContinueTarget(cur, parent, label); cur = parent, parent = parent.getParent()) { if (cur.getType() == Token.TRY && NodeUtil.hasFinally(cur)) { if (lastJump == node) { createEdge(lastJump, Branch.UNCOND, cur.getLastChild()); } else { finallyMap.put(lastJump, computeFallThrough(cur.getLastChild())); } lastJump = cur; } Preconditions.checkState(parent != null, "Cannot find continue target."); } Node iter = cur; if (cur.getChildCount() == 4) { iter = cur.getFirstChild().getNext().

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> the continue target of labeled continue. The * label can be null if it is an unlabeled continue. */ private static boolean isContinueTarget( Node target, Node parent, String label) { return isContinueStructure(target) && matchLabel(parent, label); } /** * Check if label is actually referencing the target control structure. If * label is null, it always returns true. */ private static boolean matchLabel(Node target, String label) { if (label == null) { return true; } while (target.getType() == Token.LABEL) { if (target.getFirstChild().getString().equals(label)) { return true; } target = target.getParent(); } return false; } /** * Determines if the subtree might throw an exception. */ public static boolean mayThrowException(Node n) { switch (n.getType()) { case Token.CALL: case Token.GETPROP: case Token.GETELEM: case Token.THROW: case Token.NEW: case Token.ASSIGN: case Token.INC: case Token.DEC: case Token.INSTANCEOF: return true; case Token.FUNCTION: return false; } for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (!ControlFlowGraph.isEnteringNewCfgNode(c) && mayThrowException(c)) { return true; } } return false; } /** * Determines whether the given node can be terminated with a BREAK node. */ static boolean isBreakStructure(Node n, boolean labeled) { switch (n.getType()) { case Token.FOR: case Token.DO: case Token.WHILE: case Token.SWITCH: return true; case Token.BLOCK: case Token.IF: case Token.TRY: return labeled; default: return false; } } /** * Determines whether the given node can be advanced with a CONTINUE node. */ static boolean isContinueStructure(Node n) { switch (n.getType()) { case Token.FOR: case Token.DO: case Token.WHILE: return true; default: return false; }

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>hasChildren()) { tryRemoveAssignment(t, n.getFirstChild(), state); } continue; // TODO(user): case Token.VAR: Remove var a=1;a=2;..... } tryRemoveAssignment(t, n, state); } } private void tryRemoveAssignment(NodeTraversal t, Node n, FlowState<LiveVariableLattice> state) { tryRemoveAssignment(t, n, n, state); } /** * Determines if any local variables are dead after the instruction {@code n} * and are assigned within the subtree of {@code n}. Removes those assignments * if there are any. * * @param n Target instruction. * @param exprRoot The CFG node where the liveness information in state is * still correct. * @param state The liveness information at {@code n}. */ private void tryRemoveAssignment(NodeTraversal t, Node n, Node exprRoot, FlowState<LiveVariableLattice> state) { Node parent = n.getParent(); if (NodeUtil.isAssignmentOp(n) || n.getType() == Token.INC || n.getType() == Token.DEC) { Node lhs = n.getFirstChild(); Node rhs = lhs.getNext(); // Recurse first. Example: dead_x = dead_y = 1; We try to clean up dead_y // first. if (rhs != null) { tryRemoveAssignment(t, rhs, exprRoot, state); rhs = lhs.getNext(); } Scope scope = t.getScope(); if (!NodeUtil.isName(lhs)) { return; // Not a local variable assignment. } String name = lhs.getString(); if (!scope.isDeclared(name, false)) { return; } Var var = scope.getVar(name); if (liveness.getEscapedLocals().contains(var)) { return; // Local variable that might be escaped due to closures. } // If we have an identity assignment such as a=a, always remove it // regardless of what the liveness results because it // does not change the result afterward. if (rhs != null && NodeUtil.isName(rhs) && rhs

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>.getString().equals(var.name) && NodeUtil.isAssign(n)) { n.removeChild(rhs); n.getParent().replaceChild(n, rhs); compiler.reportCodeChange(); return; } if (state.getOut().isLive(var)) { return; // Variable not dead. } if (state.getIn().isLive(var) && isVariableStillLiveWithinExpression(n, exprRoot, var.name)) { // The variable is killed here but it is also live before it. // This is possible if we have say: // if (X = a && a = C) {..} ; .......; a = S; // In this case we are safe to remove "a = C" because it is dead. // However if we have: // if (a = C && X = a) {..} ; .......; a = S; // removing "a = C" is NOT correct, although the live set at the node // is exactly the same. // TODO(user): We need more fine grain CFA or we need to keep track // of GEN sets when we recurse here. return; } if (NodeUtil.isAssign(n)) { n.removeChild(rhs); n.getParent().replaceChild(n, rhs); } else if (NodeUtil.isAssignmentOp(n)) { n.removeChild(rhs); n.removeChild(lhs); Node op = new Node(NodeUtil.getOpFromAssignmentOp(n), lhs, rhs); parent.replaceChild(n, op); } else if (n.getType() == Token.INC || n.getType() == Token.DEC) { if (NodeUtil.isExpressionNode(parent)) { parent.replaceChild(n, new Node(Token.VOID, Node.newNumber(0).copyInformationFrom(n))); } else if(n.getType() == Token.COMMA && n != parent.getLastChild()) { parent.removeChild(n); } else if (parent.getType() == Token.FOR && !NodeUtil.isForIn(parent) && NodeUtil.getConditionExpression(parent) != n) { parent.replaceChild(n, new Node(Token.EMPTY));

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> MAYBE_LIVE, // May be still live in the current expression tree. READ, // Known there is a read left of it. KILL, // Known there is a write before any read. } /** * Give an expression and a variable. It returns READ, if the right-most * reference of that variable is a read. It returns KILL, if the right-most * reference of that variable is an assignment. It returns MAY_LIVE otherwise. * * This need to be a pre-order traversal so we cannot use the normal node * traversals. */ private VariableLiveness readVariableBeforeKilling(Node n, String variable) { if (NodeUtil.isName(n) && variable.equals(n.getString())) { if (NodeUtil.isLhs(n, n.getParent())) { return VariableLiveness.KILL; } else { return VariableLiveness.READ; } } for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { if (!ControlFlowGraph.isEnteringNewCfgNode(child)) { VariableLiveness state = readVariableBeforeKilling(child, variable); if (state != VariableLiveness.MAYBE_LIVE) { return state; } } } return VariableLiveness.MAYBE_LIVE; } }

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>(ALL_TYPE); case Token.LB: // Array type // TODO(nicksantos): Enforce membership restrictions on the Array. return getNativeType(ARRAY_TYPE); case Token.PIPE: // Union type UnionTypeBuilder builder = new UnionTypeBuilder(this); for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { builder.addAlternate( createFromTypeNodesInternal(child, sourceName, scope, false)); } return builder.build(); case Token.EMPTY: // When the return value of a function is not specified return getNativeType(UNKNOWN_TYPE); case Token.VOID: // Only allowed in the return value of a function. return getNativeType(VOID_TYPE); case Token.STRING: JSType namedType = getType(scope, n.getString(), sourceName, n.getLineno(), n.getCharno()); if (forgiving) { namedType.forgiveUnknownNames(); } if (resolveMode != ResolveMode.LAZY_NAMES) { namedType = namedType.resolveInternal(reporter, scope); } if ((namedType instanceof ObjectType) && !(enumTypeNames.contains(n.getString()))) { Node typeList = n.getFirstChild(); if (typeList != null && ("Array".equals(n.getString()) || "Object".equals(n.getString()))) { JSType parameterType = createFromTypeNodesInternal( typeList.getLastChild(), sourceName, scope, false); namedType = new ParameterizedType( this, (ObjectType) namedType, parameterType); if (typeList.hasMoreThanOneChild()) { JSType indexType = createFromTypeNodesInternal( typeList.getFirstChild(), sourceName, scope, false); namedType = new IndexedType( this, (ObjectType) namedType, indexType); } } return createDefaultObjectUnion(namedType); } else { return namedType; } case Token.FUNCTION: ObjectType thisType = null; Node current = n.getFirstChild(); if (current.getType() == Token.THIS) { Node thisNode = current.getFirstChild(); thisType = ObjectType.cast( createFromTypeNodes

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> scope) { RecordTypeBuilder builder = new RecordTypeBuilder(this); // For each of the fields in the record type. for (Node fieldTypeNode = n.getFirstChild(); fieldTypeNode != null; fieldTypeNode = fieldTypeNode.getNext()) { // Get the property's name. Node fieldNameNode = fieldTypeNode; boolean hasType = false; if (fieldTypeNode.getType() == Token.COLON) { fieldNameNode = fieldTypeNode.getFirstChild(); hasType = true; } String fieldName = fieldNameNode.getString(); // TODO(user): Move this into the lexer/parser. // Remove the string literal characters around a field name, // if any. if (fieldName.startsWith("'") || fieldName.startsWith("\"")) { fieldName = fieldName.substring(1, fieldName.length() - 1); } // Get the property's type. JSType fieldType = null; if (hasType) { // We have a declared type. fieldType = createFromTypeNodesInternal( fieldTypeNode.getLastChild(), sourceName, scope, false); } else { // Otherwise, the type is UNKNOWN. fieldType = getNativeType(JSTypeNative.UNKNOWN_TYPE); } // Add the property to the record. builder.addProperty(fieldName, fieldType); } return builder.build(); } /** * Sets the template type name. */ public void setTemplateTypeName(String name) { templateTypeName = name; templateType = new TemplateType(this, name); } /** * Clears the template type name. */ public void clearTemplateTypeName() { templateTypeName = null; templateType = null; } }

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> } return false; } /** * Records a throw type's description. * * @return {@code true} if the type's description was recorded and * {@code false} if a description with the same type was already defined */ public boolean recordThrowDescription(JSTypeExpression type, String description) { if (currentInfo.documentThrows(type, description)) { populated = true; return true; } else { return false; } } /** * Adds an author to the current information. */ public boolean addAuthor(String author) { if (currentInfo.documentAuthor(author)) { populated = true; return true; } else { return false; } } /** * Adds a reference ("@see") to the current information. */ public boolean addReference(String reference) { if (currentInfo.documentReference(reference)) { populated = true; return true; } else { return false; } } /** * Records the version. */ public boolean recordVersion(String version) { if (currentInfo.documentVersion(version)) { populated = true; return true; } else { return false; } } /** * Records the deprecation reason. */ public boolean recordDeprecationReason(String reason) { if (currentInfo.setDeprecationReason(reason)) { populated = true; return true; } else { return false; } } /** * Records the list of suppressed warnings. */ public boolean recordSuppressions(Set<String> suppressions) { if (currentInfo.setSuppressions(suppressions)) { populated = true; return true; } else { return false; } } /** * Records a type. * * @return {@code true} if the type was recorded and {@code false} if * it is invalid or was already defined */ public boolean recordType(JSTypeExpression type) { if (type != null && !hasAnyTypeRelatedTags()) { currentInfo.setType(type); populated = true; return true; } else { return false; } } /** * Records that the {@link JSDoc

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>util.Map; import java.util.Set; /** * <p>JSDoc information describing JavaScript code. JSDoc is represented as a * unified object with fields for each JSDoc annotation, even though some * combinations are incorrect. For instance, if a JSDoc describes an enum, * it cannot have information about a return type. This implementation * takes advantage of such incompatibilities to reuse fields for multiple * purposes, reducing memory consumption.</p> * * <p>Constructing {@link JSDocInfo} objects is simplified by * {@link JSDocInfoBuilder} which provides early incompatibility detection.</p> * */ public final class JSDocInfo implements Serializable { private static final long serialVersionUID = 1L; /** * Visibility categories. The {@link Visibility#ordinal()} can be used as a * numerical indicator of privacy, where 0 is the most private. This means * that the {@link Visibility#compareTo} method can be used to * determine if a visibility is more permissive than another. */ public enum Visibility { PRIVATE, PROTECTED, PUBLIC, // If visibility is not specified, we just assume that visibility // is inherited from the super class. INHERITED } private static final class LazilyInitializedInfo implements Serializable { private static final long serialVersionUID = 1L; // Function information JSTypeExpression baseType = null; List<JSTypeExpression> implementedInterfaces = null; Map<String, JSTypeExpression> parameters = null; List<JSTypeExpression> thrownTypes = null; String templateTypeName = null; // Other information String description = null; String meaning = null; String deprecated = null; String license = null; Set<String> suppressions = null; } private static final class LazilyInitializedDocumentation { // TODO(nicksantos): Use UIntProps to clean up all of this. It takes // care of all the lazy-instantiation internally. List<Marker> markers = null; Map<String, String> parameters = null; Map<JSTypeExpression, String> throwsDescriptions = null; String blockDescription = null; String fileOverview = null; String returnDescription = null; String version = null;

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>markers.add(marker); return marker; } /** * Sets the deprecation reason. * * @param reason The deprecation reason */ boolean setDeprecationReason(String reason) { lazyInitInfo(); if (info.deprecated != null) { return false; } info.deprecated = reason; return true; } /** * Add a suppressed warning. */ void addSuppression(String suppression) { lazyInitInfo(); if (info.suppressions == null) { info.suppressions = Sets.newHashSet(); } info.suppressions.add(suppression); } /** * Sets suppressed warnings. * @param suppressions A list of suppressed warning types. */ boolean setSuppressions(Set<String> suppressions) { lazyInitInfo(); if (info.suppressions != null) { return false; } info.suppressions = suppressions; return true; } /** * Documents the version. */ boolean documentVersion(String version) { if (!lazyInitDocumentation()) { return true; } if (documentation.version != null) { return false; } documentation.version = version; return true; } /** * Documents a reference (i.e. adds a "see" reference to the list). */ boolean documentReference(String reference) { if (!lazyInitDocumentation()) { return true; } if (documentation.sees == null) { documentation.sees = Lists.newArrayList(); } documentation.sees.add(reference); return true; } /** * Documents the author (i.e. adds it to the author list). */ boolean documentAuthor(String author) { if (!lazyInitDocumentation()) { return true; } if (documentation.authors == null) { documentation.authors = Lists.newArrayList(); } documentation.authors.add(author); return true; } /** * Documents the throws (i.e. adds it to the throws list). */ boolean documentThrows(JSTypeExpression type, String throwsDescription) { if (!lazyInitDocumentation()) { return true; } if (documentation.throwsDescriptions == null) {

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>); } /** * Returns the list of authors or null if none. */ public Collection<String> getAuthors() { return documentation == null ? null : documentation.authors; } /** * Returns the list of references or null if none. */ public Collection<String> getReferences() { return documentation == null ? null : documentation.sees; } /** * Returns the version or null if none. */ public String getVersion() { return documentation == null ? null : documentation.version; } /** * Returns the description of the returned object or null if none specified. */ public String getReturnDescription() { return documentation == null ? null : documentation.returnDescription; } /** * Returns the block-level description or null if none specified. */ public String getBlockDescription() { return documentation == null ? null : documentation.blockDescription; } /** * Returns whether this has a fileoverview flag. */ public boolean hasFileOverview() { return getFlag(MASK_FILEOVERVIEW); } /** * Returns the file overview or null if none specified. */ public String getFileOverview() { return documentation == null ? null : documentation.fileOverview; } /** Gets the name of the source file that contains this JSDoc. */ public String getSourceName() { return sourceName; } /** Gets the list of all markers for the documentation in this JSDoc. */ public Collection<Marker> getMarkers() { return documentation == null ? null : documentation.markers; } /** Sets the name of the source file that contains this JSDoc. */ void setSourceName(String sourceName) { this.sourceName = sourceName; } /** Gets the template type name. */ public String getTemplateTypeName() { if (info == null) { return null; } return info.templateTypeName; } /** * Returns a collection of all type nodes that are a part of this JSDocInfo. * This includes @type, @this, @extends, @implements, @param, @throws, * and @return. Any future type specific JSDoc should make sure to add the * appropriate nodes here. * @return collection of all type nodes

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS>GoogNow = rewriteNewDateGoogNow; // goog is special-cased because it is provided in Closure's base library. providedNames.put(GOOG, new ProvidedName(GOOG, null, null, false /* implicit */)); } Set<String> getExportedVariableNames() { return exportedVariables; } @Override public void process(Node externs, Node root) { new NodeTraversal(compiler, this).traverse(root); for (ProvidedName pn : providedNames.values()) { pn.replace(); } if (requiresLevel.isOn()) { for (UnrecognizedRequire r : unrecognizedRequires) { DiagnosticType error; ProvidedName expectedName = providedNames.get(r.namespace); if (expectedName != null && expectedName.firstNode != null) { // The namespace ended up getting provided after it was required. error = LATE_PROVIDE_ERROR; } else { error = MISSING_PROVIDE_ERROR; } compiler.report(JSError.make( r.inputName, r.requireNode, requiresLevel, error, r.namespace)); } } } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.CALL: boolean isExpr = parent.getType() == Token.EXPR_RESULT; Node left = n.getFirstChild(); if (left.getType() == Token.GETPROP) { Node name = left.getFirstChild(); if (name.getType() == Token.NAME && GOOG.equals(name.getString())) { // For the sake of simplicity, we report code changes // when we see a provides/requires, and don't worry about // reporting the change when we actually do the replacement. String methodName = name.getNext().getString(); if ("base".equals(methodName)) { processBaseClassCall(t, n); } else if (!isExpr) { // All other methods must be called in an EXPR. break; } else if ("require".equals(methodName)) { processRequireCall(t, n, parent); } else if ("provide".equals(methodName)) { processProvideCall(t, n, parent);

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> } else if ("exportSymbol".equals(methodName)) { Node arg = left.getNext(); if (arg.getType() == Token.STRING) { int dot = arg.getString().indexOf('.'); if (dot == -1) { exportedVariables.add(arg.getString()); } else { exportedVariables.add(arg.getString().substring(0, dot)); } } } else if ("addDependency".equals(methodName)) { CodingConvention convention = compiler.getCodingConvention(); List<String> typeDecls = convention.identifyTypeDeclarationCall(n); if (typeDecls != null) { for (String typeDecl : typeDecls) { compiler.getTypeRegistry().forwardDeclareType(typeDecl); } } // We can't modify parent, so just create a node that will // get compiled out. parent.replaceChild(n, Node.newNumber(0)); compiler.reportCodeChange(); } else if ("setCssNameMapping".equals(methodName)) { processSetCssNameMapping(t, n, parent); } } } break; case Token.ASSIGN: case Token.NAME: // If this is an assignment to a provided name, remove the provided // object. handleCandidateProvideDefinition(t, n, parent); break; case Token.FUNCTION: // If this is a declaration of a provided named function, this is an // error. Hosited functions will explode if the're provided. if (t.inGlobalScope() && !NodeUtil.isFunctionExpression(n)) { String name = n.getFirstChild().getString(); ProvidedName pn = providedNames.get(name); if (pn != null) { compiler.report(t.makeError(n, FUNCTION_NAMESPACE_ERROR, name)); } } break; case Token.NEW: trySimplifyNewDate(t, n, parent); break; case Token.GETPROP: if (n.getFirstChild().getType() == Token.NAME && parent.getType() != Token.CALL && parent.getType() != Token.ASSIGN && "goog.base".equals(n.getQualifiedName())) { reportBadBaseClassUse(t, n, "May only be called directly.");

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> } break; } } /** * Handles a goog.require call. */ private void processRequireCall(NodeTraversal t, Node n, Node parent) { Node left = n.getFirstChild(); Node arg = left.getNext(); if (verifyArgument(t, left, arg)) { String ns = arg.getString(); ProvidedName provided = providedNames.get(ns); if (provided == null || !provided.isExplicitlyProvided()) { unrecognizedRequires.add( new UnrecognizedRequire(n, ns, t.getSourceName())); } else { JSModule providedModule = provided.explicitModule; // This must be non-null, because there was an explicit provide. Preconditions.checkNotNull(providedModule); JSModule module = t.getModule(); if (moduleGraph != null && module != providedModule && !moduleGraph.dependsOn(module, providedModule)) { compiler.report( t.makeError(n, XMODULE_REQUIRE_ERROR, ns, providedModule.getName(), module.getName())); } } // Requires should be removed before runtime. The one // exception is if the type has not been provided yet and // errors for broken requires are turned off, in which case, // we will be doing a later pass that may error, so we can // leave this here this time and let it error next time if it // is still not provided. if (provided != null || requiresLevel.isOn()) { parent.detachFromParent(); compiler.reportCodeChange(); } } } /** * Handles a goog.provide call. */ private void processProvideCall(NodeTraversal t, Node n, Node parent) { Node left = n.getFirstChild(); Node arg = left.getNext(); if (verifyProvide(t, left, arg)) { String ns = arg.getString(); if (providedNames.containsKey(ns)) { ProvidedName previouslyProvided = providedNames.get(ns); if (!previouslyProvided.isExplicitlyProvided()) { previouslyProvided.addProvide(parent, t.getModule(), true); } else { compiler.report( t.makeError(n, DUPLICATE_NAMESPACE_ERROR, ns)); } } else {

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> registerAnyProvidedPrefixes(ns, parent, t.getModule()); providedNames.put( ns, new ProvidedName(ns, parent, t.getModule(), true)); } } } /** * Handles a candidate definition for a goog.provided name. */ private void handleCandidateProvideDefinition( NodeTraversal t, Node n, Node parent) { if (t.inGlobalScope()) { String name = null; if (n.getType() == Token.NAME && parent.getType() == Token.VAR) { name = n.getString(); } else if (n.getType() == Token.ASSIGN && parent.getType() == Token.EXPR_RESULT) { name = n.getFirstChild().getQualifiedName(); } if (name != null) { if (parent.getBooleanProp(Node.IS_NAMESPACE)) { processProvideFromPreviousPass(t, name, parent); } else { ProvidedName pn = providedNames.get(name); if (pn != null) { pn.addDefinition(parent, t.getModule()); } } } } } /** * Processes the base class call. */ private void processBaseClassCall(NodeTraversal t, Node n) { // Two things must hold for every goog.base call: // 1) We must be calling it on "this". // 2) We must be calling it on a prototype method of the same name as // the one we're in, OR we must be calling it from a constructor. // If both of those things are true, then we can rewrite: // <pre> // function Foo() { // goog.base(this); // } // goog.inherits(Foo, BaseFoo); // Foo.prototype.bar = function() { // goog.base(this, 'bar', 1); // }; // </pre> // as the easy-to-optimize: // <pre> // function Foo() { // BaseFoo.call(this); // } // goog.inherits(Foo, BaseFoo); // Foo.prototype.bar = function() { // Foo.superClass_.bar.call(this, 1); // }; // // Most of the logic

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> here is just to make sure the AST's // structure is what we expect it to be. Node callee = n.getFirstChild(); Node thisArg = callee.getNext(); if (thisArg == null || thisArg.getType() != Token.THIS) { reportBadBaseClassUse(t, n, "First argument must be 'this'."); return; } Node enclosingFnNameNode = getEnclosingDeclNameNode(t); if (enclosingFnNameNode == null) { reportBadBaseClassUse(t, n, "Could not find enclosing method."); return; } String enclosingQname = enclosingFnNameNode.getQualifiedName(); if (enclosingQname.indexOf(".prototype.") == -1) { // Handle constructors. Node enclosingParent = enclosingFnNameNode.getParent(); Node maybeInheritsExpr = (enclosingParent.getType() == Token.ASSIGN ? enclosingParent.getParent() : enclosingParent).getNext(); Node baseClassNode = null; if (maybeInheritsExpr != null && maybeInheritsExpr.getType() == Token.EXPR_RESULT && maybeInheritsExpr.getFirstChild().getType() == Token.CALL) { Node callNode = maybeInheritsExpr.getFirstChild(); if ("goog.inherits".equals( callNode.getFirstChild().getQualifiedName()) && callNode.getLastChild().isQualifiedName()) { baseClassNode = callNode.getLastChild(); } } if (baseClassNode == null) { reportBadBaseClassUse( t, n, "Could not find goog.inherits for base class"); return; } // We're good to go. n.replaceChild( callee, NodeUtil.newQualifiedNameNode( String.format("%s.call", baseClassNode.getQualifiedName()), callee, "goog.base")); compiler.reportCodeChange(); } else { // Handle methods. Node methodNameNode = thisArg.getNext(); if (methodNameNode == null || methodNameNode.getType() != Token.STRING) { reportBadBaseClassUse(t, n, "Second argument must name a method."); return; } String methodName = methodNameNode.getString(); String ending = ".prototype." +

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> Node expr = new Node(Token.EXPR_RESULT); expr.copyInformationFromForTree(parent); parent.getParent().addChildBefore(expr, parent); compiler.reportCodeChange(); JSModule module = t.getModule(); registerAnyProvidedPrefixes(name, expr, module); ProvidedName provided = new ProvidedName(name, expr, module, true); providedNames.put(name, provided); provided.addDefinition(parent, module); } else { // Remove this provide if it came from a previous pass since we have an // replacement already. if (isNamespacePlaceholder(parent)) { parent.getParent().removeChild(parent); compiler.reportCodeChange(); } } } /** * Processes a call to goog.setCssNameMapping(). Either the argument to * goog.setCssNameMapping() is valid, in which case it will be used to create * a CssRenamingMap for the compiler of this CompilerPass, or it is invalid * and a JSCompiler error will be reported. * @see #visit(NodeTraversal, Node, Node) */ private void processSetCssNameMapping(NodeTraversal t, Node n, Node parent) { Node left = n.getFirstChild(); Node arg = left.getNext(); if (verifyArgument(t, left, arg, Token.OBJECTLIT)) { // Translate OBJECTLIT into SubstitutionMap. All keys and // values must be strings, or an error will be thrown. final Map<String, String> cssNames = Maps.newHashMap(); JSError error = null; for (Node key = arg.getFirstChild(); key != null; key = key.getNext().getNext()) { Node value = key.getNext(); if (key.getType() != Token.STRING || value == null || value.getType() != Token.STRING) { error = t.makeError(n, NON_STRING_PASSED_TO_SET_CSS_NAME_MAPPING_ERROR); } if (error != null) { compiler.report(error); break; } cssNames.put(key.getString(), value.getString()); } // If there were no errors, create a CssRenamingMap from cssNames, update // the compiler to use it and remove the

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> call to goog.setCssNameMapping(). if (error == null) { CssRenamingMap cssRenamingMap = new CssRenamingMap() { public String get(String value) { if (cssNames.containsKey(value)) { return cssNames.get(value); } else { return value; } } }; compiler.setCssRenamingMap(cssRenamingMap); parent.getParent().removeChild(parent); compiler.reportCodeChange(); } } } /** * Try to simplify "new Date(goog.now())" to "new Date()". */ private void trySimplifyNewDate(NodeTraversal t, Node n, Node parent) { if (!rewriteNewDateGoogNow) { return; } Preconditions.checkArgument(n.getType() == Token.NEW); Node date = n.getFirstChild(); if (!NodeUtil.isName(date) || !"Date".equals(date.getString())) { return; } Node callGoogNow = date.getNext(); if (callGoogNow == null || !NodeUtil.isCall(callGoogNow) || callGoogNow.getNext() != null) { return; } Node googNow = callGoogNow.getFirstChild(); String googNowQName = googNow.getQualifiedName(); if (googNowQName == null || !"goog.now".equals(googNowQName) || googNow.getNext() != null) { return; } n.removeChild(callGoogNow); compiler.reportCodeChange(); } /** * Verifies that a provide method call has exactly one argument, * and that it's a string literal and that the contents of the string are * valid JS tokens. Reports a compile error if it doesn't. * * @return Whether the argument checked out okay */ private boolean verifyProvide(NodeTraversal t, Node methodName, Node arg) { if (!verifyArgument(t, methodName, arg)) { return false; } for (String part : arg.getString().split("\\.")) { if (!NodeUtil.isValidPropertyName(part)) { compiler.report(t.makeError(arg, INVALID_PROVIDE_ERROR, part)); return false; }

Closure, 151

<FILEB>
<CHANGES>
@Option(name = "--version",
usage = "Prints the compiler version to stderr.")
private boolean version = false;
<CHANGEE>
<CHANGES>
private static final String configResource =
"com.google.javascript.jscomp.parsing.ParserConfig";
<CHANGEE>
<CHANGES>
}
if (flags.version) {
ResourceBundle config = ResourceBundle.getBundle(configResource);
err.println(
"Closure Compiler (http://code.google.com/p/closure/compiler)\n" +
"Version: " + config.getString("compiler.version") + "\n" +
"Built on: " + config.getString("compiler.date"));
err.flush();
<CHANGEE>
<FILEE>
<FILEB> + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the js output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; <CHANGES> <CHANGEE> // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler<Boolean> { private static final Set<String> TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set<String> FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) { super(parser, option, setter); } private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); <CHANGES> <CHANGEE> private boolean is<SCANS> (a = b).c = null; // We don't want to exploit the first assign. Similarly: // a.b = null; // a.b.c = null; // We don't want to exploit the first assign either. // // To protect against this, we simply only inline when the left side // is guaranteed to evaluate to the same L-value no matter what. Node leftSide = next.getFirstChild(); if (leftSide.getType() == Token.NAME || leftSide.getType() == Token.GETPROP && leftSide.getFirstChild().getType() == Token.THIS) { // Dive down the right side of the assign. parent = next; next = leftSide.getNext(); break; } else { return false; } default: // Return without inlining a thing return false; } } return false; } } /** * Checks name referenced in node to determine if it might have * changed. * @return Whether the replacement can be made. */ private boolean isSafeReplacement(Node node, Node replacement) { // No checks are needed for simple names. if (node.getType() == Token.NAME) { return true; } Preconditions.checkArgument(node.getType() == Token.GETPROP); Node name = node.getFirstChild(); if (name.getType() == Token.NAME && isNameAssignedTo(name.getString(), replacement)) { return false; } return true; } /** * @return Whether name is assigned in the expression rooted at node. */ private boolean isNameAssignedTo(String name, Node node) { for (Node c = node.getFirstChild(); c != null; c = c.getNext()) { if (isNameAssignedTo(name, c)) { return true; } } if (node.getType() == Token.NAME) { Node parent = node.getParent(); if (parent.getType() == Token.ASSIGN && parent.getFirstChild() == node) { if (name.equals(node.getString())) { return true; } } } return false; } /** * Gathers all of the variable declarations that should be collapsed into one.